ipx_pcb.c revision 21673
1214501Srpaulo/*
2214501Srpaulo * Copyright (c) 1995, Mike Mitchell
3214501Srpaulo * Copyright (c) 1984, 1985, 1986, 1987, 1993
4214501Srpaulo *	The Regents of the University of California.  All rights reserved.
5214501Srpaulo *
6214501Srpaulo * Redistribution and use in source and binary forms, with or without
7214501Srpaulo * modification, are permitted provided that the following conditions
8214501Srpaulo * are met:
9214501Srpaulo * 1. Redistributions of source code must retain the above copyright
10214501Srpaulo *    notice, this list of conditions and the following disclaimer.
11214501Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
12214501Srpaulo *    notice, this list of conditions and the following disclaimer in the
13214501Srpaulo *    documentation and/or other materials provided with the distribution.
14214501Srpaulo * 3. All advertising materials mentioning features or use of this software
15214501Srpaulo *    must display the following acknowledgement:
16214501Srpaulo *	This product includes software developed by the University of
17214501Srpaulo *	California, Berkeley and its contributors.
18214501Srpaulo * 4. Neither the name of the University nor the names of its contributors
19214501Srpaulo *    may be used to endorse or promote products derived from this software
20214501Srpaulo *    without specific prior written permission.
21214501Srpaulo *
22214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23214501Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24214501Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25214501Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26214501Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27214501Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28214501Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29214501Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30214501Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31214501Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32214501Srpaulo * SUCH DAMAGE.
33214501Srpaulo *
34214501Srpaulo *	@(#)ipx_pcb.c
35214501Srpaulo *
36214501Srpaulo * $FreeBSD: head/sys/netipx/ipx_pcb.c 21673 1997-01-14 07:20:47Z jkh $
37214501Srpaulo */
38214501Srpaulo
39214501Srpaulo#include <sys/param.h>
40214501Srpaulo#include <sys/queue.h>
41214501Srpaulo#include <sys/systm.h>
42214501Srpaulo#include <sys/mbuf.h>
43214501Srpaulo#include <sys/errno.h>
44214501Srpaulo#include <sys/socket.h>
45214501Srpaulo#include <sys/socketvar.h>
46214501Srpaulo#include <sys/protosw.h>
47214501Srpaulo
48214501Srpaulo#include <net/if.h>
49214501Srpaulo#include <net/route.h>
50214501Srpaulo
51214501Srpaulo#include <netipx/ipx.h>
52214501Srpaulo#include <netipx/ipx_if.h>
53214501Srpaulo#include <netipx/ipx_pcb.h>
54214501Srpaulo
55214501Srpaulostruct	ipx_addr zeroipx_addr;
56214501Srpaulo
57214501Srpauloint
58214501Srpauloipx_pcballoc(so, head)
59214501Srpaulo	struct socket *so;
60214501Srpaulo	struct ipxpcb *head;
61214501Srpaulo{
62214501Srpaulo	struct mbuf *m;
63214501Srpaulo	register struct ipxpcb *ipxp;
64214501Srpaulo
65214501Srpaulo	m = m_getclr(M_DONTWAIT, MT_PCB);
66214501Srpaulo	if (m == NULL)
67214501Srpaulo		return (ENOBUFS);
68214501Srpaulo	ipxp = mtod(m, struct ipxpcb *);
69214501Srpaulo	ipxp->ipxp_socket = so;
70214501Srpaulo	insque(ipxp, head);
71214501Srpaulo	so->so_pcb = (caddr_t)ipxp;
72214501Srpaulo	return (0);
73214501Srpaulo}
74214501Srpaulo
75214501Srpauloint
76214501Srpauloipx_pcbbind(ipxp, nam)
77214501Srpaulo	register struct ipxpcb *ipxp;
78214501Srpaulo	struct mbuf *nam;
79214501Srpaulo{
80214501Srpaulo	register struct sockaddr_ipx *sipx;
81214501Srpaulo	u_short lport = 0;
82214501Srpaulo
83214501Srpaulo	if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr))
84214501Srpaulo		return (EINVAL);
85214501Srpaulo	if (nam == 0)
86214501Srpaulo		goto noname;
87214501Srpaulo	sipx = mtod(nam, struct sockaddr_ipx *);
88214501Srpaulo	if (nam->m_len != sizeof (*sipx))
89214501Srpaulo		return (EINVAL);
90214501Srpaulo	if (!ipx_nullhost(sipx->sipx_addr)) {
91214501Srpaulo		int tport = sipx->sipx_port;
92214501Srpaulo
93214501Srpaulo		sipx->sipx_port = 0;		/* yech... */
94214501Srpaulo		if (ifa_ifwithaddr((struct sockaddr *)sipx) == 0)
95214501Srpaulo			return (EADDRNOTAVAIL);
96214501Srpaulo		sipx->sipx_port = tport;
97214501Srpaulo	}
98214501Srpaulo	lport = sipx->sipx_port;
99214501Srpaulo	if (lport) {
100214501Srpaulo		u_short aport = ntohs(lport);
101214501Srpaulo
102214501Srpaulo		if (aport < IPXPORT_MAX &&
103214501Srpaulo		    (ipxp->ipxp_socket->so_state & SS_PRIV) == 0)
104214501Srpaulo			return (EACCES);
105214501Srpaulo		if (ipx_pcblookup(&zeroipx_addr, lport, 0))
106214501Srpaulo			return (EADDRINUSE);
107214501Srpaulo	}
108214501Srpaulo	ipxp->ipxp_laddr = sipx->sipx_addr;
109214501Srpaulononame:
110214501Srpaulo	if (lport == 0)
111214501Srpaulo		do {
112214501Srpaulo			if (ipxpcb.ipxp_lport++ < IPXPORT_MAX)
113214501Srpaulo				ipxpcb.ipxp_lport = IPXPORT_MAX;
114214501Srpaulo			lport = htons(ipxpcb.ipxp_lport);
115214501Srpaulo		} while (ipx_pcblookup(&zeroipx_addr, lport, 0));
116214501Srpaulo	ipxp->ipxp_lport = lport;
117214501Srpaulo	return (0);
118214501Srpaulo}
119214501Srpaulo
120214501Srpaulo/*
121214501Srpaulo * Connect from a socket to a specified address.
122214501Srpaulo * Both address and port must be specified in argument sipx.
123214501Srpaulo * If don't have a local address for this socket yet,
124214501Srpaulo * then pick one.
125214501Srpaulo */
126214501Srpauloint
127214501Srpauloipx_pcbconnect(ipxp, nam)
128214501Srpaulo	struct ipxpcb *ipxp;
129214501Srpaulo	struct mbuf *nam;
130214501Srpaulo{
131214501Srpaulo	struct ipx_ifaddr *ia;
132214501Srpaulo	register struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
133214501Srpaulo	register struct ipx_addr *dst;
134214501Srpaulo	register struct route *ro;
135214501Srpaulo	struct ifnet *ifp;
136214501Srpaulo
137214501Srpaulo	if (nam->m_len != sizeof (*sipx))
138214501Srpaulo		return (EINVAL);
139214501Srpaulo	if (sipx->sipx_family != AF_IPX)
140214501Srpaulo		return (EAFNOSUPPORT);
141214501Srpaulo	if (sipx->sipx_port==0 || ipx_nullhost(sipx->sipx_addr))
142214501Srpaulo		return (EADDRNOTAVAIL);
143214501Srpaulo	/*
144214501Srpaulo	 * If we haven't bound which network number to use as ours,
145214501Srpaulo	 * we will use the number of the outgoing interface.
146214501Srpaulo	 * This depends on having done a routing lookup, which
147214501Srpaulo	 * we will probably have to do anyway, so we might
148214501Srpaulo	 * as well do it now.  On the other hand if we are
149214501Srpaulo	 * sending to multiple destinations we may have already
150214501Srpaulo	 * done the lookup, so see if we can use the route
151214501Srpaulo	 * from before.  In any case, we only
152214501Srpaulo	 * chose a port number once, even if sending to multiple
153214501Srpaulo	 * destinations.
154214501Srpaulo	 */
155214501Srpaulo	ro = &ipxp->ipxp_route;
156214501Srpaulo	dst = &satoipx_addr(ro->ro_dst);
157214501Srpaulo	if (ipxp->ipxp_socket->so_options & SO_DONTROUTE)
158214501Srpaulo		goto flush;
159214501Srpaulo	if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr))
160214501Srpaulo		goto flush;
161214501Srpaulo	if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) {
162214501Srpaulo		if (ro->ro_rt && ! (ro->ro_rt->rt_flags & RTF_HOST)) {
163214501Srpaulo			/* can patch route to avoid rtalloc */
164214501Srpaulo			*dst = sipx->sipx_addr;
165214501Srpaulo		} else {
166214501Srpaulo	flush:
167214501Srpaulo			if (ro->ro_rt)
168214501Srpaulo				RTFREE(ro->ro_rt);
169214501Srpaulo			ro->ro_rt = (struct rtentry *)0;
170214501Srpaulo			ipxp->ipxp_laddr.x_net = ipx_zeronet;
171214501Srpaulo		}
172214501Srpaulo	}/* else cached route is ok; do nothing */
173214501Srpaulo	ipxp->ipxp_lastdst = sipx->sipx_addr;
174214501Srpaulo	if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
175214501Srpaulo	    (ro->ro_rt == (struct rtentry *)0 ||
176214501Srpaulo	     ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
177214501Srpaulo		    /* No route yet, so try to acquire one */
178214501Srpaulo		    ro->ro_dst.sa_family = AF_IPX;
179214501Srpaulo		    ro->ro_dst.sa_len = sizeof(ro->ro_dst);
180214501Srpaulo		    *dst = sipx->sipx_addr;
181214501Srpaulo		    dst->x_port = 0;
182214501Srpaulo		    rtalloc(ro);
183214501Srpaulo	}
184214501Srpaulo	if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) {
185214501Srpaulo		/*
186214501Srpaulo		 * If route is known or can be allocated now,
187214501Srpaulo		 * our src addr is taken from the i/f, else punt.
188214501Srpaulo		 */
189214501Srpaulo
190214501Srpaulo		ia = (struct ipx_ifaddr *)0;
191214501Srpaulo		/*
192214501Srpaulo		 * If we found a route, use the address
193214501Srpaulo		 * corresponding to the outgoing interface
194214501Srpaulo		 */
195214501Srpaulo		if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp))
196214501Srpaulo			for (ia = ipx_ifaddr; ia; ia = ia->ia_next)
197214501Srpaulo				if (ia->ia_ifp == ifp)
198214501Srpaulo					break;
199214501Srpaulo		if (ia == 0) {
200214501Srpaulo			u_short fport = sipx->sipx_addr.x_port;
201214501Srpaulo			sipx->sipx_addr.x_port = 0;
202214501Srpaulo			ia = (struct ipx_ifaddr *)
203214501Srpaulo				ifa_ifwithdstaddr((struct sockaddr *)sipx);
204214501Srpaulo			sipx->sipx_addr.x_port = fport;
205214501Srpaulo			if (ia == 0)
206214501Srpaulo				ia = ipx_iaonnetof(&sipx->sipx_addr);
207214501Srpaulo			if (ia == 0)
208214501Srpaulo				ia = ipx_ifaddr;
209214501Srpaulo			if (ia == 0)
210214501Srpaulo				return (EADDRNOTAVAIL);
211214501Srpaulo		}
212214501Srpaulo		ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net;
213214501Srpaulo	}
214214501Srpaulo	if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
215214501Srpaulo		return (EADDRINUSE);
216214501Srpaulo	if (ipx_nullhost(ipxp->ipxp_laddr)) {
217214501Srpaulo		if (ipxp->ipxp_lport == 0)
218214501Srpaulo			(void) ipx_pcbbind(ipxp, (struct mbuf *)0);
219214501Srpaulo		ipxp->ipxp_laddr.x_host = ipx_thishost;
220214501Srpaulo	}
221214501Srpaulo	ipxp->ipxp_faddr = sipx->sipx_addr;
222214501Srpaulo	/* Includes ipxp->ipxp_fport = sipx->sipx_port; */
223214501Srpaulo	return (0);
224214501Srpaulo}
225214501Srpaulo
226214501Srpaulovoid
227214501Srpauloipx_pcbdisconnect(ipxp)
228214501Srpaulo	struct ipxpcb *ipxp;
229214501Srpaulo{
230214501Srpaulo
231214501Srpaulo	ipxp->ipxp_faddr = zeroipx_addr;
232214501Srpaulo	if (ipxp->ipxp_socket->so_state & SS_NOFDREF)
233214501Srpaulo		ipx_pcbdetach(ipxp);
234214501Srpaulo}
235214501Srpaulo
236214501Srpaulovoid
237214501Srpauloipx_pcbdetach(ipxp)
238214501Srpaulo	struct ipxpcb *ipxp;
239214501Srpaulo{
240214501Srpaulo	struct socket *so = ipxp->ipxp_socket;
241214501Srpaulo
242214501Srpaulo	so->so_pcb = 0;
243214501Srpaulo	sofree(so);
244214501Srpaulo	if (ipxp->ipxp_route.ro_rt)
245214501Srpaulo		rtfree(ipxp->ipxp_route.ro_rt);
246214501Srpaulo	remque(ipxp);
247214501Srpaulo	(void) m_free(dtom(ipxp));
248214501Srpaulo}
249214501Srpaulo
250214501Srpaulovoid
251214501Srpauloipx_setsockaddr(ipxp, nam)
252214501Srpaulo	register struct ipxpcb *ipxp;
253214501Srpaulo	struct mbuf *nam;
254214501Srpaulo{
255214501Srpaulo	register struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
256214501Srpaulo
257214501Srpaulo	nam->m_len = sizeof (*sipx);
258214501Srpaulo	sipx = mtod(nam, struct sockaddr_ipx *);
259214501Srpaulo	bzero((caddr_t)sipx, sizeof (*sipx));
260214501Srpaulo	sipx->sipx_len = sizeof(*sipx);
261214501Srpaulo	sipx->sipx_family = AF_IPX;
262214501Srpaulo	sipx->sipx_addr = ipxp->ipxp_laddr;
263214501Srpaulo}
264214501Srpaulo
265214501Srpaulovoid
266214501Srpauloipx_setpeeraddr(ipxp, nam)
267214501Srpaulo	register struct ipxpcb *ipxp;
268214501Srpaulo	struct mbuf *nam;
269214501Srpaulo{
270214501Srpaulo	register struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
271214501Srpaulo
272214501Srpaulo	nam->m_len = sizeof (*sipx);
273214501Srpaulo	sipx = mtod(nam, struct sockaddr_ipx *);
274214501Srpaulo	bzero((caddr_t)sipx, sizeof (*sipx));
275214501Srpaulo	sipx->sipx_len = sizeof(*sipx);
276214501Srpaulo	sipx->sipx_family = AF_IPX;
277214501Srpaulo	sipx->sipx_addr = ipxp->ipxp_faddr;
278214501Srpaulo}
279214501Srpaulo
280214501Srpaulo/*
281214501Srpaulo * Pass some notification to all connections of a protocol
282214501Srpaulo * associated with address dst.  Call the
283214501Srpaulo * protocol specific routine to handle each connection.
284214501Srpaulo * Also pass an extra paramter via the ipxpcb. (which may in fact
285214501Srpaulo * be a parameter list!)
286214501Srpaulo */
287214501Srpaulovoid
288214501Srpauloipx_pcbnotify(dst, errno, notify, param)
289214501Srpaulo	register struct ipx_addr *dst;
290214501Srpaulo	int errno;
291214501Srpaulo	void (*notify)(struct ipxpcb *);
292214501Srpaulo	long param;
293214501Srpaulo{
294214501Srpaulo	register struct ipxpcb *ipxp, *oinp;
295214501Srpaulo	int s = splimp();
296214501Srpaulo
297214501Srpaulo	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb);) {
298214501Srpaulo		if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) {
299214501Srpaulo	next:
300214501Srpaulo			ipxp = ipxp->ipxp_next;
301214501Srpaulo			continue;
302214501Srpaulo		}
303214501Srpaulo		if (ipxp->ipxp_socket == 0)
304214501Srpaulo			goto next;
305214501Srpaulo		if (errno)
306214501Srpaulo			ipxp->ipxp_socket->so_error = errno;
307214501Srpaulo		oinp = ipxp;
308214501Srpaulo		ipxp = ipxp->ipxp_next;
309214501Srpaulo		oinp->ipxp_notify_param = param;
310214501Srpaulo		(*notify)(oinp);
311214501Srpaulo	}
312214501Srpaulo	splx(s);
313214501Srpaulo}
314214501Srpaulo
315214501Srpaulo#ifdef notdef
316214501Srpaulo/*
317214501Srpaulo * After a routing change, flush old routing
318214501Srpaulo * and allocate a (hopefully) better one.
319214501Srpaulo */
320214501Srpauloipx_rtchange(ipxp)
321214501Srpaulo	struct ipxpcb *ipxp;
322214501Srpaulo{
323214501Srpaulo	if (ipxp->ipxp_route.ro_rt) {
324214501Srpaulo		rtfree(ipxp->ipxp_route.ro_rt);
325214501Srpaulo		ipxp->ipxp_route.ro_rt = 0;
326214501Srpaulo		/*
327214501Srpaulo		 * A new route can be allocated the next time
328214501Srpaulo		 * output is attempted.
329214501Srpaulo		 */
330214501Srpaulo	}
331214501Srpaulo	/* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
332214501Srpaulo}
333214501Srpaulo#endif
334214501Srpaulo
335214501Srpaulostruct ipxpcb *
336214501Srpauloipx_pcblookup(faddr, lport, wildp)
337214501Srpaulo	struct ipx_addr *faddr;
338214501Srpaulo	u_short lport;
339214501Srpaulo	int wildp;
340214501Srpaulo{
341214501Srpaulo	register struct ipxpcb *ipxp, *match = 0;
342214501Srpaulo	int matchwild = 3, wildcard;
343214501Srpaulo	u_short fport;
344214501Srpaulo
345214501Srpaulo	fport = faddr->x_port;
346214501Srpaulo	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb); ipxp = ipxp->ipxp_next) {
347214501Srpaulo		if (ipxp->ipxp_lport != lport)
348214501Srpaulo			continue;
349214501Srpaulo		wildcard = 0;
350214501Srpaulo		if (ipx_nullhost(ipxp->ipxp_faddr)) {
351214501Srpaulo			if (!ipx_nullhost(*faddr))
352214501Srpaulo				wildcard++;
353214501Srpaulo		} else {
354214501Srpaulo			if (ipx_nullhost(*faddr))
355214501Srpaulo				wildcard++;
356214501Srpaulo			else {
357214501Srpaulo				if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr))
358214501Srpaulo					continue;
359214501Srpaulo				if (ipxp->ipxp_fport != fport) {
360214501Srpaulo					if (ipxp->ipxp_fport != 0)
361214501Srpaulo						continue;
362214501Srpaulo					else
363214501Srpaulo						wildcard++;
364214501Srpaulo				}
365214501Srpaulo			}
366214501Srpaulo		}
367214501Srpaulo		if (wildcard && wildp==0)
368214501Srpaulo			continue;
369214501Srpaulo		if (wildcard < matchwild) {
370214501Srpaulo			match = ipxp;
371214501Srpaulo			matchwild = wildcard;
372214501Srpaulo			if (wildcard == 0)
373214501Srpaulo				break;
374214501Srpaulo		}
375214501Srpaulo	}
376214501Srpaulo	return (match);
377214501Srpaulo}
378214501Srpaulo