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