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