1 /* $OpenBSD: ipx_usrreq.c,v 1.14 2004/01/03 14:08:54 espie Exp $ */
2
3 /*-
4 *
5 * Copyright (c) 1996 Michael Shalayeff
6 * Copyright (c) 1995, Mike Mitchell
7 * Copyright (c) 1984, 1985, 1986, 1987, 1993
8 * The Regents of the University of California. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. 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 * from FreeBSD Id: ipx_usrreq.c,v 1.5 1996/03/11 15:13:57 davidg Exp
37 */
38
39 #include <sys/param.h>
40 #include <sys/queue.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/mbuf.h>
45 #include <sys/protosw.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>
48 #include <sys/errno.h>
49 #include <sys/stat.h>
50 #include <sys/proc.h>
51 #include <uvm/uvm_extern.h>
52 #include <sys/sysctl.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_pcb.h>
61 #include <netipx/ipx_if.h>
62 #include <netipx/ipx_var.h>
63 #include <netipx/ipx_ip.h>
64
65 #include <sys/stdarg.h>
66
67 /*
68 * IPX protocol implementation.
69 */
70
71 int noipxRoute;
72
73 int ipx_sendspace = IPXSNDQ;
74 int ipx_recvspace = IPXRCVQ;
75
76 /*
77 * This may also be called for raw listeners.
78 */
79 void
ipx_input(struct mbuf * m,...)80 ipx_input(struct mbuf *m, ...)
81 {
82 struct ipxpcb *ipxp;
83 struct ipx *ipx = mtod(m, struct ipx *);
84 struct ifnet *ifp = m->m_pkthdr.rcvif;
85 struct sockaddr_ipx ipx_ipx;
86 va_list ap;
87
88 va_start(ap, m);
89 ipxp = va_arg(ap, struct ipxpcb *);
90 va_end(ap);
91
92 if (ipxp == NULL)
93 panic("No ipxpcb");
94 /*
95 * Construct sockaddr format source address.
96 * Stuff source address and datagram in user buffer.
97 */
98 bzero(&ipx_ipx, sizeof(ipx_ipx));
99 ipx_ipx.sipx_len = sizeof(ipx_ipx);
100 ipx_ipx.sipx_family = AF_IPX;
101 ipx_ipx.sipx_addr = ipx->ipx_sna;
102 if (ipx_neteqnn(ipx->ipx_sna.ipx_net, ipx_zeronet) && ifp) {
103 struct ifaddr *ifa;
104
105 for (ifa = ifp->if_addrlist.tqh_first;
106 ifa; ifa = ifa->ifa_list.tqe_next) {
107 if (ifa->ifa_addr->sa_family == AF_IPX) {
108 ipx_ipx.sipx_addr.ipx_net =
109 IA_SIPX(ifa)->sipx_addr.ipx_net;
110 break;
111 }
112 }
113 }
114 ipxp->ipxp_rpt = ipx->ipx_pt;
115 if ( ! (ipxp->ipxp_flags & IPXP_RAWIN) ) {
116 m->m_len -= sizeof(struct ipx);
117 m->m_pkthdr.len -= sizeof(struct ipx);
118 m->m_data += sizeof(struct ipx);
119 }
120 if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, sipxtosa(&ipx_ipx), m,
121 (struct mbuf *)0) == 0)
122 goto bad;
123 sorwakeup(ipxp->ipxp_socket);
124 return;
125 bad:
126 m_freem(m);
127 }
128
129 void
ipx_abort(ipxp)130 ipx_abort(ipxp)
131 struct ipxpcb *ipxp;
132 {
133 struct socket *so = ipxp->ipxp_socket;
134
135 ipx_pcbdisconnect(ipxp);
136 soisdisconnected(so);
137 }
138 /*
139 * Drop connection, reporting
140 * the specified error.
141 */
142 /* struct ipxpcb * DELETE THIS */
143 void
ipx_drop(ipxp,errno)144 ipx_drop(ipxp, errno)
145 struct ipxpcb *ipxp;
146 int errno;
147 {
148 struct socket *so = ipxp->ipxp_socket;
149
150 /*
151 * someday, in the xerox world
152 * we will generate error protocol packets
153 * announcing that the socket has gone away.
154 */
155 /*if (TCPS_HAVERCVDSYN(tp->t_state)) {
156 tp->t_state = TCPS_CLOSED;
157 (void) tcp_output(tp);
158 }*/
159 so->so_error = errno;
160 ipx_pcbdisconnect(ipxp);
161 soisdisconnected(so);
162 }
163
164 int
ipx_output(struct mbuf * m0,...)165 ipx_output(struct mbuf *m0, ...)
166 {
167 struct mbuf *m;
168 struct ipx *ipx;
169 struct ipxpcb *ipxp;
170 struct socket *so;
171 int len = 0;
172 struct route *ro;
173 struct mbuf *mprev = NULL;
174 va_list ap;
175
176 va_start(ap, m0);
177 ipxp = va_arg(ap, struct ipxpcb *);
178 va_end(ap);
179
180 /*
181 * Calculate data length.
182 */
183 for (m = m0; m; m = m->m_next) {
184 mprev = m;
185 len += m->m_len;
186 }
187 /*
188 * Make sure packet is actually of even length.
189 */
190
191 if (len & 1) {
192 m = mprev;
193 if ((m->m_flags & M_EXT) == 0 &&
194 (m->m_len + m->m_data < &m->m_dat[MLEN])) {
195 m->m_len++;
196 } else {
197 struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
198
199 if (m1 == 0) {
200 m_freem(m0);
201 return (ENOBUFS);
202 }
203 m1->m_len = 1;
204 * mtod(m1, char *) = 0;
205 m->m_next = m1;
206 }
207 m0->m_pkthdr.len++;
208 }
209
210 /*
211 * Fill in mbuf with extended IPX header
212 * and addresses and length put into network format.
213 */
214 m = m0;
215 if (ipxp->ipxp_flags & IPXP_RAWOUT) {
216 ipx = mtod(m, struct ipx *);
217 } else {
218 M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT);
219 if (m == 0)
220 return (ENOBUFS);
221 ipx = mtod(m, struct ipx *);
222 ipx->ipx_tc = 0;
223 ipx->ipx_pt = ipxp->ipxp_dpt;
224 ipx->ipx_sna = ipxp->ipxp_laddr;
225 ipx->ipx_dna = ipxp->ipxp_faddr;
226 len += sizeof(struct ipx);
227 }
228
229 ipx->ipx_len = htons((u_short)len);
230
231 if (ipxcksum) {
232 ipx->ipx_sum = 0;
233 len = ((len - 1) | 1) + 1;
234 ipx->ipx_sum = ipx_cksum(m, len);
235 } else
236 ipx->ipx_sum = 0xffff;
237
238 /*
239 * Output datagram.
240 */
241 so = ipxp->ipxp_socket;
242 if (so->so_options & SO_DONTROUTE)
243 return (ipx_outputfl(m, (struct route *)0,
244 (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF));
245 /*
246 * Use cached route for previous datagram if
247 * possible. If the previous net was the same
248 * and the interface was a broadcast medium, or
249 * if the previous destination was identical,
250 * then we are ok.
251 *
252 * NB: We don't handle broadcasts because that
253 * would require 3 subroutine calls.
254 */
255 ro = &ipxp->ipxp_route;
256 if (noipxRoute)
257 ro = 0;
258 return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST));
259 }
260
261 /* ARGSUSED */
262 int
ipx_ctloutput(req,so,level,name,value)263 ipx_ctloutput(req, so, level, name, value)
264 int req, level;
265 struct socket *so;
266 int name;
267 struct mbuf **value;
268 {
269 struct mbuf *m;
270 struct ipxpcb *ipxp = sotoipxpcb(so);
271 int mask, error = 0;
272 /*extern long ipx_pexseq;*/ /*XXX*//*JRE*/
273
274 if (ipxp == NULL)
275 return (EINVAL);
276
277 switch (req) {
278
279 case PRCO_GETOPT:
280 if (value==NULL)
281 return (EINVAL);
282 m = m_get(M_DONTWAIT, MT_DATA);
283 if (m==NULL)
284 return (ENOBUFS);
285 switch (name) {
286
287 case SO_ALL_PACKETS:
288 mask = IPXP_ALL_PACKETS;
289 goto get_flags;
290
291 case SO_HEADERS_ON_INPUT:
292 mask = IPXP_RAWIN;
293 goto get_flags;
294
295 case SO_HEADERS_ON_OUTPUT:
296 mask = IPXP_RAWOUT;
297 get_flags:
298 m->m_len = sizeof(short);
299 *mtod(m, short *) = ipxp->ipxp_flags & mask;
300 break;
301
302 case SO_DEFAULT_HEADERS:
303 m->m_len = sizeof(struct ipx);
304 {
305 struct ipx *ipx = mtod(m, struct ipx *);
306 ipx->ipx_len = 0;
307 ipx->ipx_sum = 0;
308 ipx->ipx_tc = 0;
309 ipx->ipx_pt = ipxp->ipxp_dpt;
310 ipx->ipx_dna = ipxp->ipxp_faddr;
311 ipx->ipx_sna = ipxp->ipxp_laddr;
312 }
313 break;
314
315 case SO_SEQNO:
316 m->m_len = sizeof(long);
317 *mtod(m, long *) = ipx_pexseq++;
318 break;
319
320 default:
321 error = EINVAL;
322 }
323 *value = m;
324 break;
325
326 case PRCO_SETOPT:
327 switch (name) {
328 int *ok;
329
330 case SO_ALL_PACKETS:
331 mask = IPXP_ALL_PACKETS;
332 goto set_head;
333
334 case SO_HEADERS_ON_INPUT:
335 mask = IPXP_RAWIN;
336 goto set_head;
337
338 case SO_HEADERS_ON_OUTPUT:
339 mask = IPXP_RAWOUT;
340 set_head:
341 if (value && *value) {
342 ok = mtod(*value, int *);
343 if (*ok)
344 ipxp->ipxp_flags |= mask;
345 else
346 ipxp->ipxp_flags &= ~mask;
347 } else error = EINVAL;
348 break;
349
350 case SO_DEFAULT_HEADERS:
351 {
352 struct ipx *ipx
353 = mtod(*value, struct ipx *);
354 ipxp->ipxp_dpt = ipx->ipx_pt;
355 }
356 break;
357 #ifdef IPXIP
358 case SO_IPXIP_ROUTE:
359 error = ipxip_route(*value);
360 break;
361 #endif /* IPXIP */
362 #ifdef IPXTUNNEL
363 case SO_IPXTUNNEL_ROUTE:
364 error = ipxtun_route(*value);
365 break;
366 #endif
367 default:
368 error = EINVAL;
369 }
370 if (value && *value)
371 m_freem(*value);
372 break;
373 }
374 return (error);
375 }
376
377 /*ARGSUSED*/
378 int
ipx_usrreq(so,req,m,nam,control)379 ipx_usrreq(so, req, m, nam, control)
380 struct socket *so;
381 int req;
382 struct mbuf *m, *nam, *control;
383 {
384 struct ipxpcb *ipxp = sotoipxpcb(so);
385 int error = 0;
386
387 if (req == PRU_CONTROL)
388 return (ipx_control(so, (long)m, (caddr_t)nam,
389 (struct ifnet *)control));
390 if (control && control->m_len) {
391 error = EINVAL;
392 goto release;
393 }
394 if (ipxp == NULL && req != PRU_ATTACH) {
395 error = EINVAL;
396 goto release;
397 }
398 switch (req) {
399
400 case PRU_ATTACH:
401 if (ipxp != NULL) {
402 error = EINVAL;
403 break;
404 }
405 error = ipx_pcballoc(so, &ipxcbtable);
406 if (error)
407 break;
408 error = soreserve(so, ipx_sendspace, ipx_recvspace);
409 if (error)
410 break;
411 break;
412
413 case PRU_DETACH:
414 if (ipxp == NULL) {
415 error = ENOTCONN;
416 break;
417 }
418 ipx_pcbdetach(ipxp);
419 break;
420
421 case PRU_BIND:
422 error = ipx_pcbbind(ipxp, nam);
423 break;
424
425 case PRU_LISTEN:
426 error = EOPNOTSUPP;
427 break;
428
429 case PRU_CONNECT:
430 if (!ipx_nullhost(ipxp->ipxp_faddr)) {
431 error = EISCONN;
432 break;
433 }
434 error = ipx_pcbconnect(ipxp, nam);
435 if (error == 0)
436 soisconnected(so);
437 break;
438
439 case PRU_CONNECT2:
440 error = EOPNOTSUPP;
441 break;
442
443 case PRU_ACCEPT:
444 error = EOPNOTSUPP;
445 break;
446
447 case PRU_DISCONNECT:
448 if (ipx_nullhost(ipxp->ipxp_faddr)) {
449 error = ENOTCONN;
450 break;
451 }
452 ipx_pcbdisconnect(ipxp);
453 soisdisconnected(so);
454 break;
455
456 case PRU_SHUTDOWN:
457 socantsendmore(so);
458 break;
459
460 case PRU_SEND:
461 {
462 struct ipx_addr laddr;
463 int s = 0;
464
465 if (nam) {
466 laddr = ipxp->ipxp_laddr;
467 if (!ipx_nullhost(ipxp->ipxp_faddr)) {
468 error = EISCONN;
469 break;
470 }
471 /*
472 * Must block input while temporarily connected.
473 */
474 s = splnet();
475 error = ipx_pcbconnect(ipxp, nam);
476 if (error) {
477 splx(s);
478 break;
479 }
480 } else {
481 if (ipx_nullhost(ipxp->ipxp_faddr)) {
482 error = ENOTCONN;
483 break;
484 }
485 }
486 error = ipx_output(m, ipxp);
487 m = NULL;
488 if (nam) {
489 ipx_pcbdisconnect(ipxp);
490 splx(s);
491 ipxp->ipxp_laddr.ipx_host = laddr.ipx_host;
492 ipxp->ipxp_laddr.ipx_port = laddr.ipx_port;
493 }
494 }
495 break;
496
497 case PRU_ABORT:
498 ipx_pcbdetach(ipxp);
499 sofree(so);
500 soisdisconnected(so);
501 break;
502
503 case PRU_SOCKADDR:
504 ipx_setsockaddr(ipxp, nam);
505 break;
506
507 case PRU_PEERADDR:
508 ipx_setpeeraddr(ipxp, nam);
509 break;
510
511 case PRU_SENSE:
512 /*
513 * stat: don't bother with a blocksize.
514 */
515 return (0);
516
517 case PRU_SENDOOB:
518 case PRU_FASTTIMO:
519 case PRU_SLOWTIMO:
520 case PRU_PROTORCV:
521 case PRU_PROTOSEND:
522 error = EOPNOTSUPP;
523 break;
524
525 case PRU_CONTROL:
526 case PRU_RCVD:
527 case PRU_RCVOOB:
528 return (EOPNOTSUPP); /* do not free mbuf's */
529
530 default:
531 panic("ipx_usrreq");
532 }
533 release:
534 if (control != NULL)
535 m_freem(control);
536 if (m != NULL)
537 m_freem(m);
538 return (error);
539 }
540
541 /*ARGSUSED*/
542 int
ipx_raw_usrreq(so,req,m,nam,control)543 ipx_raw_usrreq(so, req, m, nam, control)
544 struct socket *so;
545 int req;
546 struct mbuf *m, *nam, *control;
547 {
548 int error = 0;
549 struct ipxpcb *ipxp = sotoipxpcb(so);
550 /*extern struct ipxpcb ipxrawpcb;*//*XXX*//*JRE*/
551
552 switch (req) {
553
554 case PRU_ATTACH:
555
556 if (!(so->so_state & SS_PRIV) || (ipxp != NULL)) {
557 error = EINVAL;
558 break;
559 }
560 error = ipx_pcballoc(so, &ipxrawcbtable);
561 if (error)
562 break;
563 error = soreserve(so, ipx_sendspace, ipx_recvspace);
564 if (error)
565 break;
566 ipxp = sotoipxpcb(so);
567 ipxp->ipxp_faddr.ipx_host = ipx_broadhost;
568 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT;
569 break;
570 default:
571 error = ipx_usrreq(so, req, m, nam, control);
572 }
573 return (error);
574 }
575
576 int
ipx_sysctl(name,namelen,oldp,oldlenp,newp,newlen)577 ipx_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
578 int *name;
579 u_int namelen;
580 void *oldp;
581 size_t *oldlenp;
582 void *newp;
583 size_t newlen;
584 {
585 /* All sysctl names at this level are terminal. */
586 if (namelen != 1)
587 return (ENOTDIR);
588
589 switch (name[0]) {
590 case IPXCTL_CHECKSUM:
591 return (sysctl_int(oldp, oldlenp, newp, newlen,
592 &ipxcksum));
593 case IPXCTL_FORWARDING:
594 return (sysctl_int(oldp, oldlenp, newp, newlen,
595 &ipxforwarding));
596 case IPXCTL_NETBIOS:
597 return (sysctl_int(oldp, oldlenp, newp, newlen,
598 &ipxnetbios));
599 case IPXCTL_RECVSPACE:
600 return (sysctl_int(oldp, oldlenp, newp, newlen,
601 &ipx_recvspace));
602 case IPXCTL_SENDSPACE:
603 return (sysctl_int(oldp, oldlenp, newp, newlen,
604 &ipx_sendspace));
605 default:
606 return (ENOPROTOOPT);
607 }
608 /* NOTREACHED */
609 }
610