]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netipx/ipx_usrreq.c
This commit was generated by cvs2svn to compensate for changes in r132873,
[FreeBSD/FreeBSD.git] / sys / netipx / 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
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include "opt_ipx.h"
41
42 #include <sys/param.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/mbuf.h>
46 #include <sys/protosw.h>
47 #include <sys/signalvar.h>
48 #include <sys/socket.h>
49 #include <sys/socketvar.h>
50 #include <sys/sx.h>
51 #include <sys/sysctl.h>
52 #include <sys/systm.h>
53
54 #include <net/if.h>
55 #include <net/route.h>
56
57 #include <netinet/in.h>
58
59 #include <netipx/ipx.h>
60 #include <netipx/ipx_if.h>
61 #include <netipx/ipx_ip.h>
62 #include <netipx/ipx_pcb.h>
63 #include <netipx/ipx_var.h>
64
65 /*
66  * IPX protocol implementation.
67  */
68
69 static int ipxsendspace = IPXSNDQ;
70 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxsendspace, CTLFLAG_RW,
71             &ipxsendspace, 0, "");
72 static int ipxrecvspace = IPXRCVQ;
73 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxrecvspace, CTLFLAG_RW,
74             &ipxrecvspace, 0, "");
75
76 static  int ipx_usr_abort(struct socket *so);
77 static  int ipx_attach(struct socket *so, int proto, struct thread *td);
78 static  int ipx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
79 static  int ipx_connect(struct socket *so, struct sockaddr *nam,
80                         struct thread *td);
81 static  int ipx_detach(struct socket *so);
82 static  int ipx_disconnect(struct socket *so);
83 static  int ipx_send(struct socket *so, int flags, struct mbuf *m,
84                      struct sockaddr *addr, struct mbuf *control, 
85                      struct thread *td);
86 static  int ipx_shutdown(struct socket *so);
87 static  int ripx_attach(struct socket *so, int proto, struct thread *td);
88 static  int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0);
89
90 struct  pr_usrreqs ipx_usrreqs = {
91         ipx_usr_abort, pru_accept_notsupp, ipx_attach, ipx_bind,
92         ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach,
93         ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp,
94         pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown,
95         ipx_sockaddr, sosend, soreceive, sopoll, pru_sosetlabel_null
96 };
97
98 struct  pr_usrreqs ripx_usrreqs = {
99         ipx_usr_abort, pru_accept_notsupp, ripx_attach, ipx_bind,
100         ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach,
101         ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp,
102         pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown,
103         ipx_sockaddr, sosend, soreceive, sopoll, pru_sosetlabel_null
104 };
105
106 /*
107  *  This may also be called for raw listeners.
108  */
109 void
110 ipx_input(m, ipxp)
111         struct mbuf *m;
112         register struct ipxpcb *ipxp;
113 {
114         register struct ipx *ipx = mtod(m, struct ipx *);
115         struct ifnet *ifp = m->m_pkthdr.rcvif;
116         struct sockaddr_ipx ipx_ipx;
117
118         if (ipxp == NULL)
119                 panic("No ipxpcb");
120         /*
121          * Construct sockaddr format source address.
122          * Stuff source address and datagram in user buffer.
123          */
124         ipx_ipx.sipx_len = sizeof(ipx_ipx);
125         ipx_ipx.sipx_family = AF_IPX;
126         ipx_ipx.sipx_addr = ipx->ipx_sna;
127         ipx_ipx.sipx_zero[0] = '\0';
128         ipx_ipx.sipx_zero[1] = '\0';
129         if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) {
130                 register struct ifaddr *ifa;
131
132                 for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa != NULL; 
133                      ifa = TAILQ_NEXT(ifa, ifa_link)) {
134                         if (ifa->ifa_addr->sa_family == AF_IPX) {
135                                 ipx_ipx.sipx_addr.x_net =
136                                         IA_SIPX(ifa)->sipx_addr.x_net;
137                                 break;
138                         }
139                 }
140         }
141         ipxp->ipxp_rpt = ipx->ipx_pt;
142         if (!(ipxp->ipxp_flags & IPXP_RAWIN) ) {
143                 m->m_len -= sizeof(struct ipx);
144                 m->m_pkthdr.len -= sizeof(struct ipx);
145                 m->m_data += sizeof(struct ipx);
146         }
147         if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, (struct sockaddr *)&ipx_ipx,
148             m, (struct mbuf *)NULL) == 0)
149                 goto bad;
150         sorwakeup(ipxp->ipxp_socket);
151         return;
152 bad:
153         m_freem(m);
154 }
155
156 void
157 ipx_abort(ipxp)
158         struct ipxpcb *ipxp;
159 {
160         struct socket *so = ipxp->ipxp_socket;
161
162         ipx_pcbdisconnect(ipxp);
163         soisdisconnected(so);
164 }
165
166 /*
167  * Drop connection, reporting
168  * the specified error.
169  */
170 void
171 ipx_drop(ipxp, errno)
172         register struct ipxpcb *ipxp;
173         int errno;
174 {
175         struct socket *so = ipxp->ipxp_socket;
176
177         /*
178          * someday, in the IPX world
179          * we will generate error protocol packets
180          * announcing that the socket has gone away.
181          *
182          * XXX Probably never. IPX does not have error packets.
183          */
184         /*if (TCPS_HAVERCVDSYN(tp->t_state)) {
185                 tp->t_state = TCPS_CLOSED;
186                 tcp_output(tp);
187         }*/
188         so->so_error = errno;
189         ipx_pcbdisconnect(ipxp);
190         soisdisconnected(so);
191 }
192
193 static int
194 ipx_output(ipxp, m0)
195         struct ipxpcb *ipxp;
196         struct mbuf *m0;
197 {
198         register struct ipx *ipx;
199         register struct socket *so;
200         register int len = 0;
201         register struct route *ro;
202         struct mbuf *m;
203         struct mbuf *mprev = NULL;
204
205         /*
206          * Calculate data length.
207          */
208         for (m = m0; m != NULL; m = m->m_next) {
209                 mprev = m;
210                 len += m->m_len;
211         }
212         /*
213          * Make sure packet is actually of even length.
214          */
215         
216         if (len & 1) {
217                 m = mprev;
218                 if ((m->m_flags & M_EXT) == 0 &&
219                         (m->m_len + m->m_data < &m->m_dat[MLEN])) {
220                         mtod(m, char*)[m->m_len++] = 0;
221                 } else {
222                         struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
223
224                         if (m1 == NULL) {
225                                 m_freem(m0);
226                                 return (ENOBUFS);
227                         }
228                         m1->m_len = 1;
229                         * mtod(m1, char *) = 0;
230                         m->m_next = m1;
231                 }
232                 m0->m_pkthdr.len++;
233         }
234
235         /*
236          * Fill in mbuf with extended IPX header
237          * and addresses and length put into network format.
238          */
239         m = m0;
240         if (ipxp->ipxp_flags & IPXP_RAWOUT) {
241                 ipx = mtod(m, struct ipx *);
242         } else {
243                 M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT);
244                 if (m == NULL)
245                         return (ENOBUFS);
246                 ipx = mtod(m, struct ipx *);
247                 ipx->ipx_tc = 0;
248                 ipx->ipx_pt = ipxp->ipxp_dpt;
249                 ipx->ipx_sna = ipxp->ipxp_laddr;
250                 ipx->ipx_dna = ipxp->ipxp_faddr;
251                 len += sizeof(struct ipx);
252         }
253
254         ipx->ipx_len = htons((u_short)len);
255
256         if (ipxp->ipxp_flags & IPXP_CHECKSUM) {
257                 ipx->ipx_sum = ipx_cksum(m, len);
258         } else
259                 ipx->ipx_sum = 0xffff;
260
261         /*
262          * Output datagram.
263          */
264         so = ipxp->ipxp_socket;
265         if (so->so_options & SO_DONTROUTE)
266                 return (ipx_outputfl(m, (struct route *)NULL,
267                     (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF));
268         /*
269          * Use cached route for previous datagram if
270          * possible.  If the previous net was the same
271          * and the interface was a broadcast medium, or
272          * if the previous destination was identical,
273          * then we are ok.
274          *
275          * NB: We don't handle broadcasts because that
276          *     would require 3 subroutine calls.
277          */
278         ro = &ipxp->ipxp_route;
279 #ifdef ancient_history
280         /*
281          * I think that this will all be handled in ipx_pcbconnect!
282          */
283         if (ro->ro_rt != NULL) {
284                 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) {
285                         /*
286                          * This assumes we have no GH type routes
287                          */
288                         if (ro->ro_rt->rt_flags & RTF_HOST) {
289                                 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna))
290                                         goto re_route;
291
292                         }
293                         if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) {
294                                 register struct ipx_addr *dst =
295                                                 &satoipx_addr(ro->ro_dst);
296                                 dst->x_host = ipx->ipx_dna.x_host;
297                         }
298                         /* 
299                          * Otherwise, we go through the same gateway
300                          * and dst is already set up.
301                          */
302                 } else {
303                 re_route:
304                         RTFREE(ro->ro_rt);
305                         ro->ro_rt = NULL;
306                 }
307         }
308         ipxp->ipxp_lastdst = ipx->ipx_dna;
309 #endif /* ancient_history */
310         return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST));
311 }
312
313 int
314 ipx_ctloutput(so, sopt)
315         struct socket *so;
316         struct sockopt *sopt;
317 {
318         struct ipxpcb *ipxp = sotoipxpcb(so);
319         int mask, error, optval;
320         short soptval;
321         struct ipx ioptval;
322
323         error = 0;
324         if (ipxp == NULL)
325                 return (EINVAL);
326
327         switch (sopt->sopt_dir) {
328         case SOPT_GET:
329                 switch (sopt->sopt_name) {
330                 case SO_ALL_PACKETS:
331                         mask = IPXP_ALL_PACKETS;
332                         goto get_flags;
333
334                 case SO_HEADERS_ON_INPUT:
335                         mask = IPXP_RAWIN;
336                         goto get_flags;
337
338                 case SO_IPX_CHECKSUM:
339                         mask = IPXP_CHECKSUM;
340                         goto get_flags;
341                         
342                 case SO_HEADERS_ON_OUTPUT:
343                         mask = IPXP_RAWOUT;
344                 get_flags:
345                         soptval = ipxp->ipxp_flags & mask;
346                         error = sooptcopyout(sopt, &soptval, sizeof soptval);
347                         break;
348
349                 case SO_DEFAULT_HEADERS:
350                         ioptval.ipx_len = 0;
351                         ioptval.ipx_sum = 0;
352                         ioptval.ipx_tc = 0;
353                         ioptval.ipx_pt = ipxp->ipxp_dpt;
354                         ioptval.ipx_dna = ipxp->ipxp_faddr;
355                         ioptval.ipx_sna = ipxp->ipxp_laddr;
356                         error = sooptcopyout(sopt, &soptval, sizeof soptval);
357                         break;
358
359                 case SO_SEQNO:
360                         error = sooptcopyout(sopt, &ipx_pexseq, 
361                                              sizeof ipx_pexseq);
362                         ipx_pexseq++;
363                         break;
364
365                 default:
366                         error = EINVAL;
367                 }
368                 break;
369
370         case SOPT_SET:
371                 switch (sopt->sopt_name) {
372                 case SO_ALL_PACKETS:
373                         mask = IPXP_ALL_PACKETS;
374                         goto set_head;
375
376                 case SO_HEADERS_ON_INPUT:
377                         mask = IPXP_RAWIN;
378                         goto set_head;
379
380                 case SO_IPX_CHECKSUM:
381                         mask = IPXP_CHECKSUM;
382
383                 case SO_HEADERS_ON_OUTPUT:
384                         mask = IPXP_RAWOUT;
385                 set_head:
386                         error = sooptcopyin(sopt, &optval, sizeof optval,
387                                             sizeof optval);
388                         if (error)
389                                 break;
390                         if (optval)
391                                 ipxp->ipxp_flags |= mask;
392                         else
393                                 ipxp->ipxp_flags &= ~mask;
394                         break;
395
396                 case SO_DEFAULT_HEADERS:
397                         error = sooptcopyin(sopt, &ioptval, sizeof ioptval,
398                                             sizeof ioptval);
399                         if (error)
400                                 break;
401                         ipxp->ipxp_dpt = ioptval.ipx_pt;
402                         break;
403 #ifdef IPXIP
404                 case SO_IPXIP_ROUTE:
405                         error = ipxip_route(so, sopt);
406                         break;
407 #endif /* IPXIP */
408                 default:
409                         error = EINVAL;
410                 }
411                 break;
412         }
413         return (error);
414 }
415
416 static int
417 ipx_usr_abort(so)
418         struct socket *so;
419 {
420         int s;
421         struct ipxpcb *ipxp = sotoipxpcb(so);
422
423         s = splnet();
424         ipx_pcbdetach(ipxp);
425         splx(s);
426         soisdisconnected(so);
427         SOCK_LOCK(so);
428         sotryfree(so);
429         return (0);
430 }
431
432 static int
433 ipx_attach(so, proto, td)
434         struct socket *so;
435         int proto;
436         struct thread *td;
437 {
438         int error;
439         int s;
440         struct ipxpcb *ipxp = sotoipxpcb(so);
441
442         if (ipxp != NULL)
443                 return (EINVAL);
444         s = splnet();
445         error = ipx_pcballoc(so, &ipxpcb, td);
446         splx(s);
447         if (error == 0)
448                 error = soreserve(so, ipxsendspace, ipxrecvspace);
449         return (error);
450 }
451
452 static int
453 ipx_bind(so, nam, td)
454         struct socket *so;
455         struct sockaddr *nam;
456         struct thread *td;
457 {
458         struct ipxpcb *ipxp = sotoipxpcb(so);
459
460         return (ipx_pcbbind(ipxp, nam, td));
461 }
462
463 static int
464 ipx_connect(so, nam, td)
465         struct socket *so;
466         struct sockaddr *nam;
467         struct thread *td;
468 {
469         int error;
470         int s;
471         struct ipxpcb *ipxp = sotoipxpcb(so);
472
473         if (!ipx_nullhost(ipxp->ipxp_faddr))
474                 return (EISCONN);
475         s = splnet();
476         error = ipx_pcbconnect(ipxp, nam, td);
477         splx(s);
478         if (error == 0)
479                 soisconnected(so);
480         return (error);
481 }
482
483 static int
484 ipx_detach(so)
485         struct socket *so;
486 {
487         int s;
488         struct ipxpcb *ipxp = sotoipxpcb(so);
489
490         if (ipxp == NULL)
491                 return (ENOTCONN);
492         s = splnet();
493         ipx_pcbdetach(ipxp);
494         splx(s);
495         return (0);
496 }
497
498 static int
499 ipx_disconnect(so)
500         struct socket *so;
501 {
502         int s;
503         struct ipxpcb *ipxp = sotoipxpcb(so);
504
505         if (ipx_nullhost(ipxp->ipxp_faddr))
506                 return (ENOTCONN);
507         s = splnet();
508         ipx_pcbdisconnect(ipxp);
509         splx(s);
510         soisdisconnected(so);
511         return (0);
512 }
513
514 int
515 ipx_peeraddr(so, nam)
516         struct socket *so;
517         struct sockaddr **nam;
518 {
519         struct ipxpcb *ipxp = sotoipxpcb(so);
520
521         ipx_setpeeraddr(ipxp, nam); /* XXX what if alloc fails? */
522         return (0);
523 }
524
525 static int
526 ipx_send(so, flags, m, nam, control, td)
527         struct socket *so;
528         int flags;
529         struct mbuf *m;
530         struct sockaddr *nam;
531         struct mbuf *control;
532         struct thread *td;
533 {
534         int error;
535         struct ipxpcb *ipxp = sotoipxpcb(so);
536         struct ipx_addr laddr;
537         int s = 0;
538
539         if (nam != NULL) {
540                 laddr = ipxp->ipxp_laddr;
541                 if (!ipx_nullhost(ipxp->ipxp_faddr)) {
542                         error = EISCONN;
543                         goto send_release;
544                 }
545                 /*
546                  * Must block input while temporarily connected.
547                  */
548                 s = splnet();
549                 error = ipx_pcbconnect(ipxp, nam, td);
550                 if (error) {
551                         splx(s);
552                         goto send_release;
553                 }
554         } else {
555                 if (ipx_nullhost(ipxp->ipxp_faddr)) {
556                         error = ENOTCONN;
557                         goto send_release;
558                 }
559         }
560         error = ipx_output(ipxp, m);
561         m = NULL;
562         if (nam != NULL) {
563                 ipx_pcbdisconnect(ipxp);
564                 splx(s);
565                 ipxp->ipxp_laddr = laddr;
566         }
567
568 send_release:
569         if (m != NULL)
570                 m_freem(m);
571         return (error);
572 }
573
574 static int
575 ipx_shutdown(so)
576         struct socket *so;
577 {
578         socantsendmore(so);
579         return (0);
580 }
581
582 int
583 ipx_sockaddr(so, nam)
584         struct socket *so;
585         struct sockaddr **nam;
586 {
587         struct ipxpcb *ipxp = sotoipxpcb(so);
588
589         ipx_setsockaddr(ipxp, nam); /* XXX what if alloc fails? */
590         return (0);
591 }
592
593 static int
594 ripx_attach(so, proto, td)
595         struct socket *so;
596         int proto;
597         struct thread *td;
598 {
599         int error = 0;
600         int s;
601         struct ipxpcb *ipxp = sotoipxpcb(so);
602
603         if (td != NULL && (error = suser(td)) != 0)
604                 return (error);
605         s = splnet();
606         error = ipx_pcballoc(so, &ipxrawpcb, td);
607         splx(s);
608         if (error)
609                 return (error);
610         error = soreserve(so, ipxsendspace, ipxrecvspace);
611         if (error)
612                 return (error);
613         ipxp = sotoipxpcb(so);
614         ipxp->ipxp_faddr.x_host = ipx_broadhost;
615         ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT;
616         return (error);
617 }