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