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