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