kernel tree reorganization stage 1: Major cvs repository work (not logged as
[dragonfly.git] / sys / netproto / ipx / ipx_usrreq.c
1 /*
2  * Copyright (c) 1995, Mike Mitchell
3  * Copyright (c) 1984, 1985, 1986, 1987, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by the University of
17  *      California, Berkeley and its contributors.
18  * 4. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *      @(#)ipx_usrreq.c
35  *
36  * $FreeBSD: src/sys/netipx/ipx_usrreq.c,v 1.26.2.1 2001/02/22 09:44:18 bp Exp $
37  * $DragonFly: src/sys/netproto/ipx/ipx_usrreq.c,v 1.5 2003/08/07 21:17:37 dillon Exp $
38  */
39
40 #include "opt_ipx.h"
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/mbuf.h>
46 #include <sys/proc.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
49 #include <sys/socketvar.h>
50 #include <sys/sysctl.h>
51
52 #include <net/if.h>
53 #include <net/route.h>
54
55 #include <netinet/in.h>
56
57 #include "ipx.h"
58 #include "ipx_pcb.h"
59 #include "ipx_if.h"
60 #include "ipx_var.h"
61 #include "ipx_ip.h"
62
63 /*
64  * IPX protocol implementation.
65  */
66
67 static int ipxsendspace = IPXSNDQ;
68 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxsendspace, CTLFLAG_RW,
69             &ipxsendspace, 0, "");
70 static int ipxrecvspace = IPXRCVQ;
71 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxrecvspace, CTLFLAG_RW,
72             &ipxrecvspace, 0, "");
73
74 static  int ipx_usr_abort(struct socket *so);
75 static  int ipx_attach(struct socket *so, int proto, struct thread *td);
76 static  int ipx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
77 static  int ipx_connect(struct socket *so, struct sockaddr *nam,
78                         struct thread *td);
79 static  int ipx_detach(struct socket *so);
80 static  int ipx_disconnect(struct socket *so);
81 static  int ipx_send(struct socket *so, int flags, struct mbuf *m,
82                      struct sockaddr *addr, struct mbuf *control, 
83                      struct thread *td);
84 static  int ipx_shutdown(struct socket *so);
85 static  int ripx_attach(struct socket *so, int proto, struct thread *td);
86 static  int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0);
87
88 struct  pr_usrreqs ipx_usrreqs = {
89         ipx_usr_abort, pru_accept_notsupp, ipx_attach, ipx_bind,
90         ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach,
91         ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp,
92         pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown,
93         ipx_sockaddr, sosend, soreceive, sopoll
94 };
95
96 struct  pr_usrreqs ripx_usrreqs = {
97         ipx_usr_abort, pru_accept_notsupp, ripx_attach, ipx_bind,
98         ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach,
99         ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp,
100         pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown,
101         ipx_sockaddr, sosend, soreceive, sopoll
102 };
103
104 /*
105  *  This may also be called for raw listeners.
106  */
107 void
108 ipx_input(m, ipxp)
109         struct mbuf *m;
110         struct ipxpcb *ipxp;
111 {
112         struct ipx *ipx = mtod(m, struct ipx *);
113         struct ifnet *ifp = m->m_pkthdr.rcvif;
114         struct sockaddr_ipx ipx_ipx;
115
116         if (ipxp == NULL)
117                 panic("No ipxpcb");
118         /*
119          * Construct sockaddr format source address.
120          * Stuff source address and datagram in user buffer.
121          */
122         ipx_ipx.sipx_len = sizeof(ipx_ipx);
123         ipx_ipx.sipx_family = AF_IPX;
124         ipx_ipx.sipx_addr = ipx->ipx_sna;
125         ipx_ipx.sipx_zero[0] = '\0';
126         ipx_ipx.sipx_zero[1] = '\0';
127         if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) {
128                 struct ifaddr *ifa;
129
130                 for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa != NULL; 
131                      ifa = TAILQ_NEXT(ifa, ifa_link)) {
132                         if (ifa->ifa_addr->sa_family == AF_IPX) {
133                                 ipx_ipx.sipx_addr.x_net =
134                                         IA_SIPX(ifa)->sipx_addr.x_net;
135                                 break;
136                         }
137                 }
138         }
139         ipxp->ipxp_rpt = ipx->ipx_pt;
140         if (!(ipxp->ipxp_flags & IPXP_RAWIN) ) {
141                 m->m_len -= sizeof(struct ipx);
142                 m->m_pkthdr.len -= sizeof(struct ipx);
143                 m->m_data += sizeof(struct ipx);
144         }
145         if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, (struct sockaddr *)&ipx_ipx,
146             m, (struct mbuf *)NULL) == 0)
147                 goto bad;
148         sorwakeup(ipxp->ipxp_socket);
149         return;
150 bad:
151         m_freem(m);
152 }
153
154 void
155 ipx_abort(ipxp)
156         struct ipxpcb *ipxp;
157 {
158         struct socket *so = ipxp->ipxp_socket;
159
160         ipx_pcbdisconnect(ipxp);
161         soisdisconnected(so);
162 }
163
164 /*
165  * Drop connection, reporting
166  * the specified error.
167  */
168 void
169 ipx_drop(ipxp, errno)
170         struct ipxpcb *ipxp;
171         int errno;
172 {
173         struct socket *so = ipxp->ipxp_socket;
174
175         /*
176          * someday, in the IPX world
177          * we will generate error protocol packets
178          * announcing that the socket has gone away.
179          *
180          * XXX Probably never. IPX does not have error packets.
181          */
182         /*if (TCPS_HAVERCVDSYN(tp->t_state)) {
183                 tp->t_state = TCPS_CLOSED;
184                 tcp_output(tp);
185         }*/
186         so->so_error = errno;
187         ipx_pcbdisconnect(ipxp);
188         soisdisconnected(so);
189 }
190
191 static int
192 ipx_output(ipxp, m0)
193         struct ipxpcb *ipxp;
194         struct mbuf *m0;
195 {
196         struct ipx *ipx;
197         struct socket *so;
198         int len = 0;
199         struct route *ro;
200         struct mbuf *m;
201         struct mbuf *mprev = NULL;
202
203         /*
204          * Calculate data length.
205          */
206         for (m = m0; m != NULL; m = m->m_next) {
207                 mprev = m;
208                 len += m->m_len;
209         }
210         /*
211          * Make sure packet is actually of even length.
212          */
213         
214         if (len & 1) {
215                 m = mprev;
216                 if ((m->m_flags & M_EXT) == 0 &&
217                         (m->m_len + m->m_data < &m->m_dat[MLEN])) {
218                         mtod(m, char*)[m->m_len++] = 0;
219                 } else {
220                         struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
221
222                         if (m1 == NULL) {
223                                 m_freem(m0);
224                                 return (ENOBUFS);
225                         }
226                         m1->m_len = 1;
227                         * mtod(m1, char *) = 0;
228                         m->m_next = m1;
229                 }
230                 m0->m_pkthdr.len++;
231         }
232
233         /*
234          * Fill in mbuf with extended IPX header
235          * and addresses and length put into network format.
236          */
237         m = m0;
238         if (ipxp->ipxp_flags & IPXP_RAWOUT) {
239                 ipx = mtod(m, struct ipx *);
240         } else {
241                 M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT);
242                 if (m == NULL)
243                         return (ENOBUFS);
244                 ipx = mtod(m, struct ipx *);
245                 ipx->ipx_tc = 0;
246                 ipx->ipx_pt = ipxp->ipxp_dpt;
247                 ipx->ipx_sna = ipxp->ipxp_laddr;
248                 ipx->ipx_dna = ipxp->ipxp_faddr;
249                 len += sizeof(struct ipx);
250         }
251
252         ipx->ipx_len = htons((u_short)len);
253
254         if (ipxp->ipxp_flags & IPXP_CHECKSUM) {
255                 ipx->ipx_sum = ipx_cksum(m, len);
256         } else
257                 ipx->ipx_sum = 0xffff;
258
259         /*
260          * Output datagram.
261          */
262         so = ipxp->ipxp_socket;
263         if (so->so_options & SO_DONTROUTE)
264                 return (ipx_outputfl(m, (struct route *)NULL,
265                     (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF));
266         /*
267          * Use cached route for previous datagram if
268          * possible.  If the previous net was the same
269          * and the interface was a broadcast medium, or
270          * if the previous destination was identical,
271          * then we are ok.
272          *
273          * NB: We don't handle broadcasts because that
274          *     would require 3 subroutine calls.
275          */
276         ro = &ipxp->ipxp_route;
277 #ifdef ancient_history
278         /*
279          * I think that this will all be handled in ipx_pcbconnect!
280          */
281         if (ro->ro_rt != NULL) {
282                 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) {
283                         /*
284                          * This assumes we have no GH type routes
285                          */
286                         if (ro->ro_rt->rt_flags & RTF_HOST) {
287                                 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna))
288                                         goto re_route;
289
290                         }
291                         if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) {
292                                 struct ipx_addr *dst =
293                                                 &satoipx_addr(ro->ro_dst);
294                                 dst->x_host = ipx->ipx_dna.x_host;
295                         }
296                         /* 
297                          * Otherwise, we go through the same gateway
298                          * and dst is already set up.
299                          */
300                 } else {
301                 re_route:
302                         RTFREE(ro->ro_rt);
303                         ro->ro_rt = NULL;
304                 }
305         }
306         ipxp->ipxp_lastdst = ipx->ipx_dna;
307 #endif /* ancient_history */
308         return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST));
309 }
310
311 int
312 ipx_ctloutput(so, sopt)
313         struct socket *so;
314         struct sockopt *sopt;
315 {
316         struct ipxpcb *ipxp = sotoipxpcb(so);
317         int mask, error, optval;
318         short soptval;
319         struct ipx ioptval;
320
321         error = 0;
322         if (ipxp == NULL)
323                 return (EINVAL);
324
325         switch (sopt->sopt_dir) {
326         case SOPT_GET:
327                 switch (sopt->sopt_name) {
328                 case SO_ALL_PACKETS:
329                         mask = IPXP_ALL_PACKETS;
330                         goto get_flags;
331
332                 case SO_HEADERS_ON_INPUT:
333                         mask = IPXP_RAWIN;
334                         goto get_flags;
335
336                 case SO_IPX_CHECKSUM:
337                         mask = IPXP_CHECKSUM;
338                         goto get_flags;
339                         
340                 case SO_HEADERS_ON_OUTPUT:
341                         mask = IPXP_RAWOUT;
342                 get_flags:
343                         soptval = ipxp->ipxp_flags & mask;
344                         error = sooptcopyout(sopt, &soptval, sizeof soptval);
345                         break;
346
347                 case SO_DEFAULT_HEADERS:
348                         ioptval.ipx_len = 0;
349                         ioptval.ipx_sum = 0;
350                         ioptval.ipx_tc = 0;
351                         ioptval.ipx_pt = ipxp->ipxp_dpt;
352                         ioptval.ipx_dna = ipxp->ipxp_faddr;
353                         ioptval.ipx_sna = ipxp->ipxp_laddr;
354                         error = sooptcopyout(sopt, &soptval, sizeof soptval);
355                         break;
356
357                 case SO_SEQNO:
358                         error = sooptcopyout(sopt, &ipx_pexseq, 
359                                              sizeof ipx_pexseq);
360                         ipx_pexseq++;
361                         break;
362
363                 default:
364                         error = EINVAL;
365                 }
366                 break;
367
368         case SOPT_SET:
369                 switch (sopt->sopt_name) {
370                 case SO_ALL_PACKETS:
371                         mask = IPXP_ALL_PACKETS;
372                         goto set_head;
373
374                 case SO_HEADERS_ON_INPUT:
375                         mask = IPXP_RAWIN;
376                         goto set_head;
377
378                 case SO_IPX_CHECKSUM:
379                         mask = IPXP_CHECKSUM;
380
381                 case SO_HEADERS_ON_OUTPUT:
382                         mask = IPXP_RAWOUT;
383                 set_head:
384                         error = sooptcopyin(sopt, &optval, sizeof optval,
385                                             sizeof optval);
386                         if (error)
387                                 break;
388                         if (optval)
389                                 ipxp->ipxp_flags |= mask;
390                         else
391                                 ipxp->ipxp_flags &= ~mask;
392                         break;
393
394                 case SO_DEFAULT_HEADERS:
395                         error = sooptcopyin(sopt, &ioptval, sizeof ioptval,
396                                             sizeof ioptval);
397                         if (error)
398                                 break;
399                         ipxp->ipxp_dpt = ioptval.ipx_pt;
400                         break;
401 #ifdef IPXIP
402                 case SO_IPXIP_ROUTE:
403                         error = ipxip_route(so, sopt);
404                         break;
405 #endif /* IPXIP */
406 #ifdef IPTUNNEL
407 #if 0
408                 case SO_IPXTUNNEL_ROUTE:
409                         error = ipxtun_route(so, sopt);
410                         break;
411 #endif
412 #endif
413                 default:
414                         error = EINVAL;
415                 }
416                 break;
417         }
418         return (error);
419 }
420
421 static int
422 ipx_usr_abort(so)
423         struct socket *so;
424 {
425         int s;
426         struct ipxpcb *ipxp = sotoipxpcb(so);
427
428         s = splnet();
429         ipx_pcbdetach(ipxp);
430         splx(s);
431         sofree(so);
432         soisdisconnected(so);
433         return (0);
434 }
435
436 static int
437 ipx_attach(struct socket *so, int proto, struct thread *td)
438 {
439         int error;
440         int s;
441         struct ipxpcb *ipxp = sotoipxpcb(so);
442
443         if (ipxp != NULL)
444                 return (EINVAL);
445         s = splnet();
446         error = ipx_pcballoc(so, &ipxpcb, td);
447         splx(s);
448         if (error == 0)
449                 error = soreserve(so, ipxsendspace, ipxrecvspace);
450         return (error);
451 }
452
453 static int
454 ipx_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
455 {
456         struct ipxpcb *ipxp = sotoipxpcb(so);
457
458         return (ipx_pcbbind(ipxp, nam, td));
459 }
460
461 static int
462 ipx_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
463 {
464         int error;
465         int s;
466         struct ipxpcb *ipxp = sotoipxpcb(so);
467
468         if (!ipx_nullhost(ipxp->ipxp_faddr))
469                 return (EISCONN);
470         s = splnet();
471         error = ipx_pcbconnect(ipxp, nam, td);
472         splx(s);
473         if (error == 0)
474                 soisconnected(so);
475         return (error);
476 }
477
478 static int
479 ipx_detach(so)
480         struct socket *so;
481 {
482         int s;
483         struct ipxpcb *ipxp = sotoipxpcb(so);
484
485         if (ipxp == NULL)
486                 return (ENOTCONN);
487         s = splnet();
488         ipx_pcbdetach(ipxp);
489         splx(s);
490         return (0);
491 }
492
493 static int
494 ipx_disconnect(so)
495         struct socket *so;
496 {
497         int s;
498         struct ipxpcb *ipxp = sotoipxpcb(so);
499
500         if (ipx_nullhost(ipxp->ipxp_faddr))
501                 return (ENOTCONN);
502         s = splnet();
503         ipx_pcbdisconnect(ipxp);
504         splx(s);
505         soisdisconnected(so);
506         return (0);
507 }
508
509 int
510 ipx_peeraddr(so, nam)
511         struct socket *so;
512         struct sockaddr **nam;
513 {
514         struct ipxpcb *ipxp = sotoipxpcb(so);
515
516         ipx_setpeeraddr(ipxp, nam); /* XXX what if alloc fails? */
517         return (0);
518 }
519
520 static int
521 ipx_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
522         struct mbuf *control, struct thread *td)
523 {
524         int error;
525         struct ipxpcb *ipxp = sotoipxpcb(so);
526         struct ipx_addr laddr;
527         int s = 0;
528
529         if (nam != NULL) {
530                 laddr = ipxp->ipxp_laddr;
531                 if (!ipx_nullhost(ipxp->ipxp_faddr)) {
532                         error = EISCONN;
533                         goto send_release;
534                 }
535                 /*
536                  * Must block input while temporarily connected.
537                  */
538                 s = splnet();
539                 error = ipx_pcbconnect(ipxp, nam, td);
540                 if (error) {
541                         splx(s);
542                         goto send_release;
543                 }
544         } else {
545                 if (ipx_nullhost(ipxp->ipxp_faddr)) {
546                         error = ENOTCONN;
547                         goto send_release;
548                 }
549         }
550         error = ipx_output(ipxp, m);
551         m = NULL;
552         if (nam != NULL) {
553                 ipx_pcbdisconnect(ipxp);
554                 splx(s);
555                 ipxp->ipxp_laddr = laddr;
556         }
557
558 send_release:
559         if (m != NULL)
560                 m_freem(m);
561         return (error);
562 }
563
564 static int
565 ipx_shutdown(so)
566         struct socket *so;
567 {
568         socantsendmore(so);
569         return (0);
570 }
571
572 int
573 ipx_sockaddr(so, nam)
574         struct socket *so;
575         struct sockaddr **nam;
576 {
577         struct ipxpcb *ipxp = sotoipxpcb(so);
578
579         ipx_setsockaddr(ipxp, nam); /* XXX what if alloc fails? */
580         return (0);
581 }
582
583 static int
584 ripx_attach(struct socket *so, int proto, struct thread *td)
585 {
586         int error = 0;
587         int s;
588         struct ipxpcb *ipxp = sotoipxpcb(so);
589
590         if ((error = suser(td)) != 0)
591                 return (error);
592         s = splnet();
593         error = ipx_pcballoc(so, &ipxrawpcb, td);
594         splx(s);
595         if (error)
596                 return (error);
597         error = soreserve(so, ipxsendspace, ipxrecvspace);
598         if (error)
599                 return (error);
600         ipxp = sotoipxpcb(so);
601         ipxp->ipxp_faddr.x_host = ipx_broadhost;
602         ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT;
603         return (error);
604 }