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