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