Merge from vendor branch BZIP:
[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.9 2005/06/10 22:34:49 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 #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(m, ipxp)
113         struct mbuf *m;
114         struct ipxpcb *ipxp;
115 {
116         struct ipx *ipx = mtod(m, struct ipx *);
117         struct ifnet *ifp = m->m_pkthdr.rcvif;
118         struct sockaddr_ipx ipx_ipx;
119
120         if (ipxp == NULL)
121                 panic("No ipxpcb");
122         /*
123          * Construct sockaddr format source address.
124          * Stuff source address and datagram in user buffer.
125          */
126         ipx_ipx.sipx_len = sizeof(ipx_ipx);
127         ipx_ipx.sipx_family = AF_IPX;
128         ipx_ipx.sipx_addr = ipx->ipx_sna;
129         ipx_ipx.sipx_zero[0] = '\0';
130         ipx_ipx.sipx_zero[1] = '\0';
131         if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) {
132                 struct ifaddr *ifa;
133
134                 for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa != NULL; 
135                      ifa = TAILQ_NEXT(ifa, ifa_link)) {
136                         if (ifa->ifa_addr->sa_family == AF_IPX) {
137                                 ipx_ipx.sipx_addr.x_net =
138                                         IA_SIPX(ifa)->sipx_addr.x_net;
139                                 break;
140                         }
141                 }
142         }
143         ipxp->ipxp_rpt = ipx->ipx_pt;
144         if (!(ipxp->ipxp_flags & IPXP_RAWIN) ) {
145                 m->m_len -= sizeof(struct ipx);
146                 m->m_pkthdr.len -= sizeof(struct ipx);
147                 m->m_data += sizeof(struct ipx);
148         }
149         if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, (struct sockaddr *)&ipx_ipx,
150             m, (struct mbuf *)NULL) == 0)
151                 goto bad;
152         sorwakeup(ipxp->ipxp_socket);
153         return;
154 bad:
155         m_freem(m);
156 }
157
158 void
159 ipx_abort(ipxp)
160         struct ipxpcb *ipxp;
161 {
162         struct socket *so = ipxp->ipxp_socket;
163
164         ipx_pcbdisconnect(ipxp);
165         soisdisconnected(so);
166 }
167
168 /*
169  * Drop connection, reporting
170  * the specified error.
171  */
172 void
173 ipx_drop(ipxp, errno)
174         struct ipxpcb *ipxp;
175         int errno;
176 {
177         struct socket *so = ipxp->ipxp_socket;
178
179         /*
180          * someday, in the IPX world
181          * we will generate error protocol packets
182          * announcing that the socket has gone away.
183          *
184          * XXX Probably never. IPX does not have error packets.
185          */
186         /*if (TCPS_HAVERCVDSYN(tp->t_state)) {
187                 tp->t_state = TCPS_CLOSED;
188                 tcp_output(tp);
189         }*/
190         so->so_error = errno;
191         ipx_pcbdisconnect(ipxp);
192         soisdisconnected(so);
193 }
194
195 static int
196 ipx_output(ipxp, m0)
197         struct ipxpcb *ipxp;
198         struct mbuf *m0;
199 {
200         struct ipx *ipx;
201         struct socket *so;
202         int len = 0;
203         struct route *ro;
204         struct mbuf *m;
205         struct mbuf *mprev = NULL;
206
207         /*
208          * Calculate data length.
209          */
210         for (m = m0; m != NULL; m = m->m_next) {
211                 mprev = m;
212                 len += m->m_len;
213         }
214         /*
215          * Make sure packet is actually of even length.
216          */
217         
218         if (len & 1) {
219                 m = mprev;
220                 if ((m->m_flags & M_EXT) == 0 &&
221                         (m->m_len + m->m_data < &m->m_dat[MLEN])) {
222                         mtod(m, char*)[m->m_len++] = 0;
223                 } else {
224                         struct mbuf *m1 = m_get(MB_DONTWAIT, MT_DATA);
225
226                         if (m1 == NULL) {
227                                 m_freem(m0);
228                                 return (ENOBUFS);
229                         }
230                         m1->m_len = 1;
231                         * mtod(m1, char *) = 0;
232                         m->m_next = m1;
233                 }
234                 m0->m_pkthdr.len++;
235         }
236
237         /*
238          * Fill in mbuf with extended IPX header
239          * and addresses and length put into network format.
240          */
241         m = m0;
242         if (ipxp->ipxp_flags & IPXP_RAWOUT) {
243                 ipx = mtod(m, struct ipx *);
244         } else {
245                 M_PREPEND(m, sizeof(struct ipx), MB_DONTWAIT);
246                 if (m == NULL)
247                         return (ENOBUFS);
248                 ipx = mtod(m, struct ipx *);
249                 ipx->ipx_tc = 0;
250                 ipx->ipx_pt = ipxp->ipxp_dpt;
251                 ipx->ipx_sna = ipxp->ipxp_laddr;
252                 ipx->ipx_dna = ipxp->ipxp_faddr;
253                 len += sizeof(struct ipx);
254         }
255
256         ipx->ipx_len = htons((u_short)len);
257
258         if (ipxp->ipxp_flags & IPXP_CHECKSUM) {
259                 ipx->ipx_sum = ipx_cksum(m, len);
260         } else
261                 ipx->ipx_sum = 0xffff;
262
263         /*
264          * Output datagram.
265          */
266         so = ipxp->ipxp_socket;
267         if (so->so_options & SO_DONTROUTE)
268                 return (ipx_outputfl(m, (struct route *)NULL,
269                     (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF));
270         /*
271          * Use cached route for previous datagram if
272          * possible.  If the previous net was the same
273          * and the interface was a broadcast medium, or
274          * if the previous destination was identical,
275          * then we are ok.
276          *
277          * NB: We don't handle broadcasts because that
278          *     would require 3 subroutine calls.
279          */
280         ro = &ipxp->ipxp_route;
281 #ifdef ancient_history
282         /*
283          * I think that this will all be handled in ipx_pcbconnect!
284          */
285         if (ro->ro_rt != NULL) {
286                 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) {
287                         /*
288                          * This assumes we have no GH type routes
289                          */
290                         if (ro->ro_rt->rt_flags & RTF_HOST) {
291                                 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna))
292                                         goto re_route;
293
294                         }
295                         if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) {
296                                 struct ipx_addr *dst =
297                                                 &satoipx_addr(ro->ro_dst);
298                                 dst->x_host = ipx->ipx_dna.x_host;
299                         }
300                         /* 
301                          * Otherwise, we go through the same gateway
302                          * and dst is already set up.
303                          */
304                 } else {
305                 re_route:
306                         RTFREE(ro->ro_rt);
307                         ro->ro_rt = NULL;
308                 }
309         }
310         ipxp->ipxp_lastdst = ipx->ipx_dna;
311 #endif /* ancient_history */
312         return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST));
313 }
314
315 int
316 ipx_ctloutput(so, sopt)
317         struct socket *so;
318         struct sockopt *sopt;
319 {
320         struct ipxpcb *ipxp = sotoipxpcb(so);
321         int mask, error, optval;
322         short soptval;
323         struct ipx ioptval;
324
325         error = 0;
326         if (ipxp == NULL)
327                 return (EINVAL);
328
329         switch (sopt->sopt_dir) {
330         case SOPT_GET:
331                 switch (sopt->sopt_name) {
332                 case SO_ALL_PACKETS:
333                         mask = IPXP_ALL_PACKETS;
334                         goto get_flags;
335
336                 case SO_HEADERS_ON_INPUT:
337                         mask = IPXP_RAWIN;
338                         goto get_flags;
339
340                 case SO_IPX_CHECKSUM:
341                         mask = IPXP_CHECKSUM;
342                         goto get_flags;
343                         
344                 case SO_HEADERS_ON_OUTPUT:
345                         mask = IPXP_RAWOUT;
346                 get_flags:
347                         soptval = ipxp->ipxp_flags & mask;
348                         error = sooptcopyout(sopt, &soptval, sizeof soptval);
349                         break;
350
351                 case SO_DEFAULT_HEADERS:
352                         ioptval.ipx_len = 0;
353                         ioptval.ipx_sum = 0;
354                         ioptval.ipx_tc = 0;
355                         ioptval.ipx_pt = ipxp->ipxp_dpt;
356                         ioptval.ipx_dna = ipxp->ipxp_faddr;
357                         ioptval.ipx_sna = ipxp->ipxp_laddr;
358                         error = sooptcopyout(sopt, &soptval, sizeof soptval);
359                         break;
360
361                 case SO_SEQNO:
362                         error = sooptcopyout(sopt, &ipx_pexseq, 
363                                              sizeof ipx_pexseq);
364                         ipx_pexseq++;
365                         break;
366
367                 default:
368                         error = EINVAL;
369                 }
370                 break;
371
372         case SOPT_SET:
373                 switch (sopt->sopt_name) {
374                 case SO_ALL_PACKETS:
375                         mask = IPXP_ALL_PACKETS;
376                         goto set_head;
377
378                 case SO_HEADERS_ON_INPUT:
379                         mask = IPXP_RAWIN;
380                         goto set_head;
381
382                 case SO_IPX_CHECKSUM:
383                         mask = IPXP_CHECKSUM;
384
385                 case SO_HEADERS_ON_OUTPUT:
386                         mask = IPXP_RAWOUT;
387                 set_head:
388                         error = sooptcopyin(sopt, &optval, sizeof optval,
389                                             sizeof optval);
390                         if (error)
391                                 break;
392                         if (optval)
393                                 ipxp->ipxp_flags |= mask;
394                         else
395                                 ipxp->ipxp_flags &= ~mask;
396                         break;
397
398                 case SO_DEFAULT_HEADERS:
399                         error = sooptcopyin(sopt, &ioptval, sizeof ioptval,
400                                             sizeof ioptval);
401                         if (error)
402                                 break;
403                         ipxp->ipxp_dpt = ioptval.ipx_pt;
404                         break;
405 #ifdef IPXIP
406                 case SO_IPXIP_ROUTE:
407                         error = ipxip_route(so, sopt);
408                         break;
409 #endif /* IPXIP */
410 #ifdef IPTUNNEL
411 #if 0
412                 case SO_IPXTUNNEL_ROUTE:
413                         error = ipxtun_route(so, sopt);
414                         break;
415 #endif
416 #endif
417                 default:
418                         error = EINVAL;
419                 }
420                 break;
421         }
422         return (error);
423 }
424
425 static int
426 ipx_usr_abort(so)
427         struct socket *so;
428 {
429         struct ipxpcb *ipxp = sotoipxpcb(so);
430
431         crit_enter();
432         ipx_pcbdetach(ipxp);
433         crit_exit();
434         sofree(so);
435         soisdisconnected(so);
436         return (0);
437 }
438
439 static int
440 ipx_attach(struct socket *so, int proto, struct pru_attach_info *ai)
441 {
442         int error;
443         struct ipxpcb *ipxp = sotoipxpcb(so);
444
445         if (ipxp != NULL)
446                 return (EINVAL);
447         crit_enter();
448         error = ipx_pcballoc(so, &ipxpcb);
449         crit_exit();
450         if (error == 0)
451                 error = soreserve(so, ipxsendspace, ipxrecvspace,
452                                   ai->sb_rlimit);
453         return (error);
454 }
455
456 static int
457 ipx_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
458 {
459         struct ipxpcb *ipxp = sotoipxpcb(so);
460
461         return (ipx_pcbbind(ipxp, nam, td));
462 }
463
464 static int
465 ipx_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
466 {
467         int error;
468         struct ipxpcb *ipxp = sotoipxpcb(so);
469
470         if (!ipx_nullhost(ipxp->ipxp_faddr))
471                 return (EISCONN);
472         crit_enter();
473         error = ipx_pcbconnect(ipxp, nam, td);
474         crit_exit();
475         if (error == 0)
476                 soisconnected(so);
477         return (error);
478 }
479
480 static int
481 ipx_detach(so)
482         struct socket *so;
483 {
484         struct ipxpcb *ipxp = sotoipxpcb(so);
485
486         if (ipxp == NULL)
487                 return (ENOTCONN);
488         crit_enter();
489         ipx_pcbdetach(ipxp);
490         crit_exit();
491         return (0);
492 }
493
494 static int
495 ipx_disconnect(so)
496         struct socket *so;
497 {
498         struct ipxpcb *ipxp = sotoipxpcb(so);
499
500         if (ipx_nullhost(ipxp->ipxp_faddr))
501                 return (ENOTCONN);
502         crit_enter();
503         ipx_pcbdisconnect(ipxp);
504         crit_exit();
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);
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
528         crit_enter();
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                 error = ipx_pcbconnect(ipxp, nam, td);
539                 if (error) {
540                         goto send_release;
541                 }
542         } else {
543                 if (ipx_nullhost(ipxp->ipxp_faddr)) {
544                         error = ENOTCONN;
545                         goto send_release;
546                 }
547         }
548         error = ipx_output(ipxp, m);
549         m = NULL;
550         if (nam != NULL) {
551                 ipx_pcbdisconnect(ipxp);
552                 ipxp->ipxp_laddr = laddr;
553         }
554 send_release:
555         crit_exit();
556         if (m != NULL)
557                 m_freem(m);
558         return (error);
559 }
560
561 static int
562 ipx_shutdown(so)
563         struct socket *so;
564 {
565         socantsendmore(so);
566         return (0);
567 }
568
569 int
570 ipx_sockaddr(so, nam)
571         struct socket *so;
572         struct sockaddr **nam;
573 {
574         struct ipxpcb *ipxp = sotoipxpcb(so);
575
576         ipx_setsockaddr(ipxp, nam); /* XXX what if alloc fails? */
577         return (0);
578 }
579
580 static int
581 ripx_attach(struct socket *so, int proto, struct pru_attach_info *ai)
582 {
583         int error = 0;
584         struct ipxpcb *ipxp = sotoipxpcb(so);
585
586         if ((error = suser_cred(ai->p_ucred, NULL_CRED_OKAY)) != 0)
587                 return (error);
588         crit_enter();
589         error = ipx_pcballoc(so, &ipxrawpcb);
590         crit_exit();
591         if (error)
592                 return (error);
593         error = soreserve(so, ipxsendspace, ipxrecvspace, ai->sb_rlimit);
594         if (error)
595                 return (error);
596         ipxp = sotoipxpcb(so);
597         ipxp->ipxp_faddr.x_host = ipx_broadhost;
598         ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT;
599         return (error);
600 }