ipx_usrreq.c revision 26965
1114402Sru/*
2114402Sru * Copyright (c) 1995, Mike Mitchell
3114402Sru * Copyright (c) 1984, 1985, 1986, 1987, 1993
4114402Sru *	The Regents of the University of California.  All rights reserved.
5114402Sru *
6114402Sru * Redistribution and use in source and binary forms, with or without
7114402Sru * modification, are permitted provided that the following conditions
8114402Sru * are met:
9114402Sru * 1. Redistributions of source code must retain the above copyright
10114402Sru *    notice, this list of conditions and the following disclaimer.
11114402Sru * 2. Redistributions in binary form must reproduce the above copyright
12114402Sru *    notice, this list of conditions and the following disclaimer in the
13114402Sru *    documentation and/or other materials provided with the distribution.
14114402Sru * 3. All advertising materials mentioning features or use of this software
15114402Sru *    must display the following acknowledgement:
16114402Sru *	This product includes software developed by the University of
17114402Sru *	California, Berkeley and its contributors.
18114402Sru * 4. Neither the name of the University nor the names of its contributors
19151503Sru *    may be used to endorse or promote products derived from this software
20114402Sru *    without specific prior written permission.
21114411Sru *
22114411Sru * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23114402Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24114411Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25114411Sru * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26114411Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27114402Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28114402Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29114402Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30114402Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31114402Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32114411Sru * SUCH DAMAGE.
33114402Sru *
34114402Sru *	@(#)ipx_usrreq.c
35114402Sru *
36114402Sru * $Id: ipx_usrreq.c,v 1.14 1997/05/10 09:58:55 jhay Exp $
37114402Sru */
38114402Sru
39114402Sru#include <sys/param.h>
40114402Sru#include <sys/systm.h>
41114402Sru#include <sys/kernel.h>
42114402Sru#include <sys/mbuf.h>
43114402Sru#include <sys/proc.h>
44114402Sru#include <sys/protosw.h>
45114402Sru#include <sys/socket.h>
46114402Sru#include <sys/socketvar.h>
47114402Sru#include <sys/sysctl.h>
48114402Sru
49114402Sru#include <net/if.h>
50114402Sru#include <net/route.h>
51114402Sru
52114402Sru#include <netinet/in.h>
53114402Sru
54114402Sru#include <netipx/ipx.h>
55114402Sru#include <netipx/ipx_pcb.h>
56114402Sru#include <netipx/ipx_if.h>
57114411Sru#include <netipx/ipx_var.h>
58114411Sru#include <netipx/ipx_ip.h>
59114411Sru
60114402Sru/*
61114402Sru * IPX protocol implementation.
62114402Sru */
63114402Sru
64114402Sruint ipxsendspace = IPXSNDQ;
65SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxsendspace, CTLFLAG_RW,
66            &ipxsendspace, 0, "");
67int ipxrecvspace = IPXRCVQ;
68SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxrecvspace, CTLFLAG_RW,
69            &ipxrecvspace, 0, "");
70
71static	int ipx_usr_abort(struct socket *so);
72static	int ipx_attach(struct socket *so, int proto, struct proc *p);
73static	int ipx_bind(struct socket *so, struct mbuf *nam, struct proc *p);
74static	int ipx_connect(struct socket *so, struct mbuf *nam, struct proc *p);
75static	int ipx_detach(struct socket *so);
76static	int ipx_disconnect(struct socket *so);
77static	int ipx_send(struct socket *so, int flags, struct mbuf *m,
78		     struct mbuf *addr, struct mbuf *control, struct proc *p);
79static	int ipx_shutdown(struct socket *so);
80static	int ripx_attach(struct socket *so, int proto, struct proc *p);
81static	int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0);
82
83struct	pr_usrreqs ipx_usrreqs = {
84	ipx_usr_abort, pru_accept_notsupp, ipx_attach, ipx_bind,
85	ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach,
86	ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp,
87	pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown,
88	ipx_sockaddr, sosend, soreceive, soselect
89};
90
91struct	pr_usrreqs ripx_usrreqs = {
92	ipx_usr_abort, pru_accept_notsupp, ripx_attach, ipx_bind,
93	ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach,
94	ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp,
95	pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown,
96	ipx_sockaddr, sosend, soreceive, soselect
97};
98
99/*
100 *  This may also be called for raw listeners.
101 */
102void
103ipx_input(m, ipxp)
104	struct mbuf *m;
105	register struct ipxpcb *ipxp;
106{
107	register struct ipx *ipx = mtod(m, struct ipx *);
108	struct ifnet *ifp = m->m_pkthdr.rcvif;
109	struct sockaddr_ipx ipx_ipx;
110
111	if (ipxp == NULL)
112		panic("No ipxpcb");
113	/*
114	 * Construct sockaddr format source address.
115	 * Stuff source address and datagram in user buffer.
116	 */
117	ipx_ipx.sipx_len = sizeof(ipx_ipx);
118	ipx_ipx.sipx_family = AF_IPX;
119	ipx_ipx.sipx_addr = ipx->ipx_sna;
120	ipx_ipx.sipx_zero[0] = '\0';
121	ipx_ipx.sipx_zero[1] = '\0';
122	if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) {
123		register struct ifaddr *ifa;
124
125		for (ifa = ifp->if_addrhead.tqh_first; ifa != NULL;
126		     ifa = ifa->ifa_link.tqe_next) {
127			if (ifa->ifa_addr->sa_family == AF_IPX) {
128				ipx_ipx.sipx_addr.x_net =
129					IA_SIPX(ifa)->sipx_addr.x_net;
130				break;
131			}
132		}
133	}
134	ipxp->ipxp_rpt = ipx->ipx_pt;
135	if (!(ipxp->ipxp_flags & IPXP_RAWIN) ) {
136		m->m_len -= sizeof(struct ipx);
137		m->m_pkthdr.len -= sizeof(struct ipx);
138		m->m_data += sizeof(struct ipx);
139	}
140	if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, (struct sockaddr *)&ipx_ipx,
141	    m, (struct mbuf *)NULL) == 0)
142		goto bad;
143	sorwakeup(ipxp->ipxp_socket);
144	return;
145bad:
146	m_freem(m);
147}
148
149void
150ipx_abort(ipxp)
151	struct ipxpcb *ipxp;
152{
153	struct socket *so = ipxp->ipxp_socket;
154
155	ipx_pcbdisconnect(ipxp);
156	soisdisconnected(so);
157}
158
159/*
160 * Drop connection, reporting
161 * the specified error.
162 */
163void
164ipx_drop(ipxp, errno)
165	register struct ipxpcb *ipxp;
166	int errno;
167{
168	struct socket *so = ipxp->ipxp_socket;
169
170	/*
171	 * someday, in the IPX world
172	 * we will generate error protocol packets
173	 * announcing that the socket has gone away.
174	 *
175	 * XXX Probably never. IPX does not have error packets.
176	 */
177	/*if (TCPS_HAVERCVDSYN(tp->t_state)) {
178		tp->t_state = TCPS_CLOSED;
179		tcp_output(tp);
180	}*/
181	so->so_error = errno;
182	ipx_pcbdisconnect(ipxp);
183	soisdisconnected(so);
184}
185
186static int
187ipx_output(ipxp, m0)
188	struct ipxpcb *ipxp;
189	struct mbuf *m0;
190{
191	register struct mbuf *m;
192	register struct ipx *ipx;
193	register struct socket *so;
194	register int len = 0;
195	register struct route *ro;
196	struct mbuf *mprev = NULL;
197
198	/*
199	 * Calculate data length.
200	 */
201	for (m = m0; m != NULL; m = m->m_next) {
202		mprev = m;
203		len += m->m_len;
204	}
205	/*
206	 * Make sure packet is actually of even length.
207	 */
208
209	if (len & 1) {
210		m = mprev;
211		if ((m->m_flags & M_EXT) == 0 &&
212			(m->m_len + m->m_data < &m->m_dat[MLEN])) {
213			m->m_len++;
214		} else {
215			struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
216
217			if (m1 == NULL) {
218				m_freem(m0);
219				return (ENOBUFS);
220			}
221			m1->m_len = 1;
222			* mtod(m1, char *) = 0;
223			m->m_next = m1;
224		}
225		m0->m_pkthdr.len++;
226	}
227
228	/*
229	 * Fill in mbuf with extended IPX header
230	 * and addresses and length put into network format.
231	 */
232	m = m0;
233	if (ipxp->ipxp_flags & IPXP_RAWOUT) {
234		ipx = mtod(m, struct ipx *);
235	} else {
236		M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT);
237		if (m == NULL)
238			return (ENOBUFS);
239		ipx = mtod(m, struct ipx *);
240		ipx->ipx_tc = 0;
241		ipx->ipx_pt = ipxp->ipxp_dpt;
242		ipx->ipx_sna = ipxp->ipxp_laddr;
243		ipx->ipx_dna = ipxp->ipxp_faddr;
244		len += sizeof(struct ipx);
245	}
246
247	ipx->ipx_len = htons((u_short)len);
248
249	if (ipxcksum) {
250		ipx->ipx_sum = 0;
251		len = ((len - 1) | 1) + 1;
252		ipx->ipx_sum = ipx_cksum(m, len);
253	} else
254		ipx->ipx_sum = 0xffff;
255
256	/*
257	 * Output datagram.
258	 */
259	so = ipxp->ipxp_socket;
260	if (so->so_options & SO_DONTROUTE)
261		return (ipx_outputfl(m, (struct route *)NULL,
262		    (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF));
263	/*
264	 * Use cached route for previous datagram if
265	 * possible.  If the previous net was the same
266	 * and the interface was a broadcast medium, or
267	 * if the previous destination was identical,
268	 * then we are ok.
269	 *
270	 * NB: We don't handle broadcasts because that
271	 *     would require 3 subroutine calls.
272	 */
273	ro = &ipxp->ipxp_route;
274#ifdef ancient_history
275	/*
276	 * I think that this will all be handled in ipx_pcbconnect!
277	 */
278	if (ro->ro_rt != NULL) {
279		if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) {
280			/*
281			 * This assumes we have no GH type routes
282			 */
283			if (ro->ro_rt->rt_flags & RTF_HOST) {
284				if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna))
285					goto re_route;
286
287			}
288			if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) {
289				register struct ipx_addr *dst =
290						&satoipx_addr(ro->ro_dst);
291				dst->x_host = ipx->ipx_dna.x_host;
292			}
293			/*
294			 * Otherwise, we go through the same gateway
295			 * and dst is already set up.
296			 */
297		} else {
298		re_route:
299			RTFREE(ro->ro_rt);
300			ro->ro_rt = NULL;
301		}
302	}
303	ipxp->ipxp_lastdst = ipx->ipx_dna;
304#endif /* ancient_history */
305	return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST));
306}
307
308int
309ipx_ctloutput(req, so, level, name, value, p)
310	int req, level;
311	struct socket *so;
312	int name;
313	struct mbuf **value;
314	struct proc *p;
315{
316	register struct mbuf *m;
317	struct ipxpcb *ipxp = sotoipxpcb(so);
318	int mask, error = 0;
319
320	if (ipxp == NULL)
321		return (EINVAL);
322
323	switch (req) {
324
325	case PRCO_GETOPT:
326		if (value == NULL)
327			return (EINVAL);
328		m = m_get(M_DONTWAIT, MT_DATA);
329		if (m == NULL)
330			return (ENOBUFS);
331		switch (name) {
332
333		case SO_ALL_PACKETS:
334			mask = IPXP_ALL_PACKETS;
335			goto get_flags;
336
337		case SO_HEADERS_ON_INPUT:
338			mask = IPXP_RAWIN;
339			goto get_flags;
340
341		case SO_HEADERS_ON_OUTPUT:
342			mask = IPXP_RAWOUT;
343		get_flags:
344			m->m_len = sizeof(short);
345			*mtod(m, short *) = ipxp->ipxp_flags & mask;
346			break;
347
348		case SO_DEFAULT_HEADERS:
349			m->m_len = sizeof(struct ipx);
350			{
351				register struct ipx *ipx = mtod(m, struct ipx *);
352				ipx->ipx_len = 0;
353				ipx->ipx_sum = 0;
354				ipx->ipx_tc = 0;
355				ipx->ipx_pt = ipxp->ipxp_dpt;
356				ipx->ipx_dna = ipxp->ipxp_faddr;
357				ipx->ipx_sna = ipxp->ipxp_laddr;
358			}
359			break;
360
361		case SO_SEQNO:
362			m->m_len = sizeof(long);
363			*mtod(m, long *) = ipx_pexseq++;
364			break;
365
366		default:
367			error = EINVAL;
368		}
369		*value = m;
370		break;
371
372	case PRCO_SETOPT:
373		switch (name) {
374			int *ok;
375
376		case SO_ALL_PACKETS:
377			mask = IPXP_ALL_PACKETS;
378			goto set_head;
379
380		case SO_HEADERS_ON_INPUT:
381			mask = IPXP_RAWIN;
382			goto set_head;
383
384		case SO_HEADERS_ON_OUTPUT:
385			mask = IPXP_RAWOUT;
386		set_head:
387			if (value && *value) {
388				ok = mtod(*value, int *);
389				if (*ok)
390					ipxp->ipxp_flags |= mask;
391				else
392					ipxp->ipxp_flags &= ~mask;
393			} else error = EINVAL;
394			break;
395
396		case SO_DEFAULT_HEADERS:
397			{
398				register struct ipx *ipx
399				    = mtod(*value, struct ipx *);
400				ipxp->ipxp_dpt = ipx->ipx_pt;
401			}
402			break;
403#ifdef IPXIP
404		case SO_IPXIP_ROUTE:
405			error = ipxip_route(so, *value, p);
406			break;
407#endif /* IPXIP */
408#ifdef IPXTUNNEL
409		case SO_IPXTUNNEL_ROUTE
410			error = ipxtun_route(so, *value, p);
411			break;
412#endif
413		default:
414			error = EINVAL;
415		}
416		if (value && *value)
417			m_freem(*value);
418		break;
419	}
420	return (error);
421}
422
423static int
424ipx_usr_abort(so)
425	struct socket *so;
426{
427	int s;
428	struct ipxpcb *ipxp = sotoipxpcb(so);
429
430	s = splnet();
431	ipx_pcbdetach(ipxp);
432	splx(s);
433	sofree(so);
434	soisdisconnected(so);
435	return (0);
436}
437
438static int
439ipx_attach(so, proto, p)
440	struct socket *so;
441	int proto;
442	struct proc *p;
443{
444	int error;
445	int s;
446	struct ipxpcb *ipxp = sotoipxpcb(so);
447
448	if (ipxp != NULL)
449		return (EINVAL);
450	s = splnet();
451	error = ipx_pcballoc(so, &ipxpcb, p);
452	splx(s);
453	if (error == 0)
454		error = soreserve(so, ipxsendspace, ipxrecvspace);
455	return (error);
456}
457
458static int
459ipx_bind(so, nam, p)
460	struct socket *so;
461	struct mbuf *nam;
462	struct proc *p;
463{
464	struct ipxpcb *ipxp = sotoipxpcb(so);
465
466	return (ipx_pcbbind(ipxp, nam, p));
467}
468
469static int
470ipx_connect(so, nam, p)
471	struct socket *so;
472	struct mbuf *nam;
473	struct proc *p;
474{
475	int error;
476	int s;
477	struct ipxpcb *ipxp = sotoipxpcb(so);
478
479	if (!ipx_nullhost(ipxp->ipxp_faddr))
480		return (EISCONN);
481	s = splnet();
482	error = ipx_pcbconnect(ipxp, nam, p);
483	splx(s);
484	if (error == 0)
485		soisconnected(so);
486	return (error);
487}
488
489static int
490ipx_detach(so)
491	struct socket *so;
492{
493	int s;
494	struct ipxpcb *ipxp = sotoipxpcb(so);
495
496	if (ipxp == NULL)
497		return (ENOTCONN);
498	s = splnet();
499	ipx_pcbdetach(ipxp);
500	splx(s);
501	return (0);
502}
503
504static int
505ipx_disconnect(so)
506	struct socket *so;
507{
508	int s;
509	struct ipxpcb *ipxp = sotoipxpcb(so);
510
511	if (ipx_nullhost(ipxp->ipxp_faddr))
512		return (ENOTCONN);
513	s = splnet();
514	ipx_pcbdisconnect(ipxp);
515	splx(s);
516	soisdisconnected(so);
517	return (0);
518}
519
520int
521ipx_peeraddr(so, nam)
522	struct socket *so;
523	struct mbuf *nam;
524{
525	struct ipxpcb *ipxp = sotoipxpcb(so);
526
527	ipx_setpeeraddr(ipxp, nam);
528	return (0);
529}
530
531static int
532ipx_send(so, flags, m, nam, control, p)
533	struct socket *so;
534	int flags;
535	struct mbuf *m;
536	struct mbuf *nam;
537	struct mbuf *control;
538	struct proc *p;
539{
540	int error;
541	struct ipxpcb *ipxp = sotoipxpcb(so);
542	struct ipx_addr laddr;
543	int s = 0;
544
545	if (nam != NULL) {
546		laddr = ipxp->ipxp_laddr;
547		if (!ipx_nullhost(ipxp->ipxp_faddr)) {
548			error = EISCONN;
549			goto send_release;
550		}
551		/*
552		 * Must block input while temporarily connected.
553		 */
554		s = splnet();
555		error = ipx_pcbconnect(ipxp, nam, p);
556		if (error) {
557			splx(s);
558			goto send_release;
559		}
560	} else {
561		if (ipx_nullhost(ipxp->ipxp_faddr)) {
562			error = ENOTCONN;
563			goto send_release;
564		}
565	}
566	error = ipx_output(ipxp, m);
567	m = NULL;
568	if (nam != NULL) {
569		ipx_pcbdisconnect(ipxp);
570		splx(s);
571		ipxp->ipxp_laddr.x_host = laddr.x_host;
572		ipxp->ipxp_laddr.x_port = laddr.x_port;
573	}
574
575send_release:
576	if (m != NULL)
577		m_freem(m);
578	return (error);
579}
580
581static int
582ipx_shutdown(so)
583	struct socket *so;
584{
585	socantsendmore(so);
586	return (0);
587}
588
589int
590ipx_sockaddr(so, nam)
591	struct socket *so;
592	struct mbuf *nam;
593{
594	struct ipxpcb *ipxp = sotoipxpcb(so);
595
596	ipx_setsockaddr(ipxp, nam);
597	return (0);
598}
599
600static int
601ripx_attach(so, proto, p)
602	struct socket *so;
603	int proto;
604	struct proc *p;
605{
606	int error = 0;
607	int s;
608	struct ipxpcb *ipxp = sotoipxpcb(so);
609
610	if (p != NULL && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
611		return (error);
612	s = splnet();
613	error = ipx_pcballoc(so, &ipxrawpcb, p);
614	splx(s);
615	if (error)
616		return (error);
617	error = soreserve(so, ipxsendspace, ipxrecvspace);
618	if (error)
619		return (error);
620	ipxp = sotoipxpcb(so);
621	ipxp->ipxp_faddr.x_host = ipx_broadhost;
622	ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT;
623	return (error);
624}
625