ipx_pcb.c revision 96972
1167612Ssimon/*
2167612Ssimon * Copyright (c) 1995, Mike Mitchell
3167612Ssimon * Copyright (c) 1984, 1985, 1986, 1987, 1993
4167612Ssimon *	The Regents of the University of California.  All rights reserved.
5167612Ssimon *
6167612Ssimon * Redistribution and use in source and binary forms, with or without
7167612Ssimon * modification, are permitted provided that the following conditions
8167612Ssimon * are met:
9167612Ssimon * 1. Redistributions of source code must retain the above copyright
10167612Ssimon *    notice, this list of conditions and the following disclaimer.
11167612Ssimon * 2. Redistributions in binary form must reproduce the above copyright
12167612Ssimon *    notice, this list of conditions and the following disclaimer in the
13167612Ssimon *    documentation and/or other materials provided with the distribution.
14167612Ssimon * 3. All advertising materials mentioning features or use of this software
15167612Ssimon *    must display the following acknowledgement:
16167612Ssimon *	This product includes software developed by the University of
17167612Ssimon *	California, Berkeley and its contributors.
18167612Ssimon * 4. Neither the name of the University nor the names of its contributors
19167612Ssimon *    may be used to endorse or promote products derived from this software
20167612Ssimon *    without specific prior written permission.
21167612Ssimon *
22167612Ssimon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23167612Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24167612Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25167612Ssimon * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26167612Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27167612Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28167612Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29167612Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30167612Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31167612Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32167612Ssimon * SUCH DAMAGE.
33167612Ssimon *
34167612Ssimon *	@(#)ipx_pcb.c
35167612Ssimon *
36167612Ssimon * $FreeBSD: head/sys/netipx/ipx_pcb.c 96972 2002-05-20 05:41:09Z tanimura $
37167612Ssimon */
38167612Ssimon
39167612Ssimon#include <sys/param.h>
40167612Ssimon#include <sys/systm.h>
41167612Ssimon#include <sys/malloc.h>
42167612Ssimon#include <sys/socket.h>
43167612Ssimon#include <sys/socketvar.h>
44167612Ssimon
45167612Ssimon#include <net/if.h>
46167612Ssimon#include <net/route.h>
47167612Ssimon
48167612Ssimon#include <netipx/ipx.h>
49167612Ssimon#include <netipx/ipx_if.h>
50167612Ssimon#include <netipx/ipx_pcb.h>
51167612Ssimon#include <netipx/ipx_var.h>
52167612Ssimon
53167612Ssimonstatic struct	ipx_addr zeroipx_addr;
54167612Ssimon
55167612Ssimonint
56167612Ssimonipx_pcballoc(so, head, td)
57167612Ssimon	struct socket *so;
58167612Ssimon	struct ipxpcb *head;
59167612Ssimon	struct thread *td;
60167612Ssimon{
61167612Ssimon	register struct ipxpcb *ipxp;
62167612Ssimon
63167612Ssimon	MALLOC(ipxp, struct ipxpcb *, sizeof *ipxp, M_PCB, M_NOWAIT | M_ZERO);
64167612Ssimon	if (ipxp == NULL)
65167612Ssimon		return (ENOBUFS);
66167612Ssimon	ipxp->ipxp_socket = so;
67167612Ssimon	if (ipxcksum)
68167612Ssimon		ipxp->ipxp_flags |= IPXP_CHECKSUM;
69167612Ssimon	insque(ipxp, head);
70167612Ssimon	so->so_pcb = (caddr_t)ipxp;
71167612Ssimon	return (0);
72167612Ssimon}
73167612Ssimon
74167612Ssimonint
75167612Ssimonipx_pcbbind(ipxp, nam, td)
76167612Ssimon	register struct ipxpcb *ipxp;
77167612Ssimon	struct sockaddr *nam;
78167612Ssimon	struct thread *td;
79167612Ssimon{
80167612Ssimon	register struct sockaddr_ipx *sipx;
81167612Ssimon	u_short lport = 0;
82167612Ssimon
83167612Ssimon	if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr))
84167612Ssimon		return (EINVAL);
85167612Ssimon	if (nam == NULL)
86167612Ssimon		goto noname;
87167612Ssimon	sipx = (struct sockaddr_ipx *)nam;
88167612Ssimon	if (!ipx_nullhost(sipx->sipx_addr)) {
89167612Ssimon		int tport = sipx->sipx_port;
90167612Ssimon
91167612Ssimon		sipx->sipx_port = 0;		/* yech... */
92167612Ssimon		if (ifa_ifwithaddr((struct sockaddr *)sipx) == 0)
93167612Ssimon			return (EADDRNOTAVAIL);
94167612Ssimon		sipx->sipx_port = tport;
95167612Ssimon	}
96167612Ssimon	lport = sipx->sipx_port;
97167612Ssimon	if (lport) {
98167612Ssimon		u_short aport = ntohs(lport);
99167612Ssimon		int error;
100167612Ssimon
101167612Ssimon		if (aport < IPXPORT_RESERVED &&
102167612Ssimon		    td != NULL && (error = suser(td)) != 0)
103167612Ssimon			return (error);
104167612Ssimon		if (ipx_pcblookup(&zeroipx_addr, lport, 0))
105167612Ssimon			return (EADDRINUSE);
106167612Ssimon	}
107167612Ssimon	ipxp->ipxp_laddr = sipx->sipx_addr;
108167612Ssimonnoname:
109167612Ssimon	if (lport == 0)
110167612Ssimon		do {
111167612Ssimon			ipxpcb.ipxp_lport++;
112167612Ssimon			if ((ipxpcb.ipxp_lport < IPXPORT_RESERVED) ||
113167612Ssimon			    (ipxpcb.ipxp_lport >= IPXPORT_WELLKNOWN))
114167612Ssimon				ipxpcb.ipxp_lport = IPXPORT_RESERVED;
115167612Ssimon			lport = htons(ipxpcb.ipxp_lport);
116167612Ssimon		} while (ipx_pcblookup(&zeroipx_addr, lport, 0));
117167612Ssimon	ipxp->ipxp_lport = lport;
118167612Ssimon	return (0);
119167612Ssimon}
120167612Ssimon
121167612Ssimon/*
122167612Ssimon * Connect from a socket to a specified address.
123167612Ssimon * Both address and port must be specified in argument sipx.
124167612Ssimon * If don't have a local address for this socket yet,
125167612Ssimon * then pick one.
126167612Ssimon */
127167612Ssimonint
128167612Ssimonipx_pcbconnect(ipxp, nam, td)
129167612Ssimon	struct ipxpcb *ipxp;
130167612Ssimon	struct sockaddr *nam;
131167612Ssimon	struct thread *td;
132167612Ssimon{
133167612Ssimon	struct ipx_ifaddr *ia;
134167612Ssimon	register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)nam;
135167612Ssimon	register struct ipx_addr *dst;
136167612Ssimon	register struct route *ro;
137167612Ssimon	struct ifnet *ifp;
138167612Ssimon
139167612Ssimon	ia = NULL;
140167612Ssimon
141167612Ssimon	if (sipx->sipx_family != AF_IPX)
142167612Ssimon		return (EAFNOSUPPORT);
143167612Ssimon	if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr))
144167612Ssimon		return (EADDRNOTAVAIL);
145167612Ssimon	/*
146167612Ssimon	 * If we haven't bound which network number to use as ours,
147167612Ssimon	 * we will use the number of the outgoing interface.
148167612Ssimon	 * This depends on having done a routing lookup, which
149167612Ssimon	 * we will probably have to do anyway, so we might
150167612Ssimon	 * as well do it now.  On the other hand if we are
151167612Ssimon	 * sending to multiple destinations we may have already
152167612Ssimon	 * done the lookup, so see if we can use the route
153167612Ssimon	 * from before.  In any case, we only
154167612Ssimon	 * chose a port number once, even if sending to multiple
155167612Ssimon	 * destinations.
156167612Ssimon	 */
157167612Ssimon	ro = &ipxp->ipxp_route;
158167612Ssimon	dst = &satoipx_addr(ro->ro_dst);
159167612Ssimon	SOCK_LOCK(ipxp->ipxp_socket);
160167612Ssimon	if (ipxp->ipxp_socket->so_options & SO_DONTROUTE) {
161167612Ssimon		SOCK_UNLOCK(ipxp->ipxp_socket);
162167612Ssimon		goto flush;
163167612Ssimon	}
164167612Ssimon	SOCK_UNLOCK(ipxp->ipxp_socket);
165167612Ssimon	if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr))
166167612Ssimon		goto flush;
167167612Ssimon	if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) {
168167612Ssimon		if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) {
169167612Ssimon			/* can patch route to avoid rtalloc */
170167612Ssimon			*dst = sipx->sipx_addr;
171167612Ssimon		} else {
172167612Ssimon	flush:
173167612Ssimon			if (ro->ro_rt != NULL)
174167612Ssimon				RTFREE(ro->ro_rt);
175167612Ssimon			ro->ro_rt = NULL;
176167612Ssimon		}
177167612Ssimon	}/* else cached route is ok; do nothing */
178167612Ssimon	ipxp->ipxp_lastdst = sipx->sipx_addr;
179167612Ssimon	SOCK_LOCK(ipxp->ipxp_socket);
180167612Ssimon	if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
181167612Ssimon	    (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
182167612Ssimon		SOCK_UNLOCK(ipxp->ipxp_socket);
183167612Ssimon		    /* No route yet, so try to acquire one */
184167612Ssimon		    ro->ro_dst.sa_family = AF_IPX;
185167612Ssimon		    ro->ro_dst.sa_len = sizeof(ro->ro_dst);
186167612Ssimon		    *dst = sipx->sipx_addr;
187167612Ssimon		    dst->x_port = 0;
188167612Ssimon		    rtalloc(ro);
189167612Ssimon	} else
190167612Ssimon		SOCK_UNLOCK(ipxp->ipxp_socket);
191167612Ssimon	if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) {
192167612Ssimon		/*
193167612Ssimon		 * If route is known or can be allocated now,
194167612Ssimon		 * our src addr is taken from the i/f, else punt.
195167612Ssimon		 */
196167612Ssimon
197167612Ssimon		/*
198167612Ssimon		 * If we found a route, use the address
199167612Ssimon		 * corresponding to the outgoing interface
200167612Ssimon		 */
201167612Ssimon		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
202167612Ssimon			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
203167612Ssimon				if (ia->ia_ifp == ifp)
204167612Ssimon					break;
205167612Ssimon		if (ia == NULL) {
206167612Ssimon			u_short fport = sipx->sipx_addr.x_port;
207167612Ssimon			sipx->sipx_addr.x_port = 0;
208167612Ssimon			ia = (struct ipx_ifaddr *)
209167612Ssimon				ifa_ifwithdstaddr((struct sockaddr *)sipx);
210167612Ssimon			sipx->sipx_addr.x_port = fport;
211167612Ssimon			if (ia == NULL)
212167612Ssimon				ia = ipx_iaonnetof(&sipx->sipx_addr);
213167612Ssimon			if (ia == NULL)
214167612Ssimon				ia = ipx_ifaddr;
215167612Ssimon			if (ia == NULL)
216167612Ssimon				return (EADDRNOTAVAIL);
217167612Ssimon		}
218167612Ssimon		ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net;
219167612Ssimon	}
220167612Ssimon	if (ipx_nullhost(ipxp->ipxp_laddr)) {
221167612Ssimon		/*
222167612Ssimon		 * If route is known or can be allocated now,
223167612Ssimon		 * our src addr is taken from the i/f, else punt.
224167612Ssimon		 */
225167612Ssimon
226167612Ssimon		/*
227167612Ssimon		 * If we found a route, use the address
228167612Ssimon		 * corresponding to the outgoing interface
229167612Ssimon		 */
230167612Ssimon		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
231167612Ssimon			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
232167612Ssimon				if (ia->ia_ifp == ifp)
233167612Ssimon					break;
234167612Ssimon		if (ia == NULL) {
235167612Ssimon			u_short fport = sipx->sipx_addr.x_port;
236167612Ssimon			sipx->sipx_addr.x_port = 0;
237167612Ssimon			ia = (struct ipx_ifaddr *)
238167612Ssimon				ifa_ifwithdstaddr((struct sockaddr *)sipx);
239167612Ssimon			sipx->sipx_addr.x_port = fport;
240167612Ssimon			if (ia == NULL)
241167612Ssimon				ia = ipx_iaonnetof(&sipx->sipx_addr);
242167612Ssimon			if (ia == NULL)
243167612Ssimon				ia = ipx_ifaddr;
244167612Ssimon			if (ia == NULL)
245167612Ssimon				return (EADDRNOTAVAIL);
246167612Ssimon		}
247167612Ssimon		ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host;
248167612Ssimon	}
249167612Ssimon	if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
250167612Ssimon		return (EADDRINUSE);
251167612Ssimon	if (ipxp->ipxp_lport == 0)
252167612Ssimon		ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td);
253167612Ssimon
254167612Ssimon	/* XXX just leave it zero if we can't find a route */
255167612Ssimon
256167612Ssimon	ipxp->ipxp_faddr = sipx->sipx_addr;
257167612Ssimon	/* Includes ipxp->ipxp_fport = sipx->sipx_port; */
258167612Ssimon	return (0);
259167612Ssimon}
260167612Ssimon
261167612Ssimonvoid
262167612Ssimonipx_pcbdisconnect(ipxp)
263167612Ssimon	struct ipxpcb *ipxp;
264167612Ssimon{
265167612Ssimon
266167612Ssimon	ipxp->ipxp_faddr = zeroipx_addr;
267167612Ssimon	SOCK_LOCK(ipxp->ipxp_socket);
268167612Ssimon	if (ipxp->ipxp_socket->so_state & SS_NOFDREF) {
269167612Ssimon		SOCK_UNLOCK(ipxp->ipxp_socket);
270167612Ssimon		ipx_pcbdetach(ipxp);
271167612Ssimon	} else
272167612Ssimon		SOCK_UNLOCK(ipxp->ipxp_socket);
273167612Ssimon}
274167612Ssimon
275167612Ssimonvoid
276167612Ssimonipx_pcbdetach(ipxp)
277167612Ssimon	struct ipxpcb *ipxp;
278167612Ssimon{
279167612Ssimon	struct socket *so = ipxp->ipxp_socket;
280167612Ssimon
281167612Ssimon	so->so_pcb = 0;
282167612Ssimon	SOCK_LOCK(so);
283167612Ssimon	sotryfree(so);
284167612Ssimon	if (ipxp->ipxp_route.ro_rt != NULL)
285167612Ssimon		rtfree(ipxp->ipxp_route.ro_rt);
286167612Ssimon	remque(ipxp);
287167612Ssimon	FREE(ipxp, M_PCB);
288167612Ssimon}
289167612Ssimon
290167612Ssimonvoid
291167612Ssimonipx_setsockaddr(ipxp, nam)
292167612Ssimon	register struct ipxpcb *ipxp;
293167612Ssimon	struct sockaddr **nam;
294167612Ssimon{
295167612Ssimon	struct sockaddr_ipx *sipx, ssipx;
296167612Ssimon
297167612Ssimon	sipx = &ssipx;
298167612Ssimon	bzero((caddr_t)sipx, sizeof(*sipx));
299167612Ssimon	sipx->sipx_len = sizeof(*sipx);
300167612Ssimon	sipx->sipx_family = AF_IPX;
301167612Ssimon	sipx->sipx_addr = ipxp->ipxp_laddr;
302167612Ssimon	*nam = dup_sockaddr((struct sockaddr *)sipx, 0);
303167612Ssimon}
304167612Ssimon
305167612Ssimonvoid
306167612Ssimonipx_setpeeraddr(ipxp, nam)
307167612Ssimon	register struct ipxpcb *ipxp;
308167612Ssimon	struct sockaddr **nam;
309167612Ssimon{
310167612Ssimon	struct sockaddr_ipx *sipx, ssipx;
311167612Ssimon
312167612Ssimon	sipx = &ssipx;
313167612Ssimon	bzero((caddr_t)sipx, sizeof(*sipx));
314167612Ssimon	sipx->sipx_len = sizeof(*sipx);
315167612Ssimon	sipx->sipx_family = AF_IPX;
316167612Ssimon	sipx->sipx_addr = ipxp->ipxp_faddr;
317167612Ssimon	*nam = dup_sockaddr((struct sockaddr *)sipx, 0);
318167612Ssimon}
319167612Ssimon
320167612Ssimon/*
321167612Ssimon * Pass some notification to all connections of a protocol
322167612Ssimon * associated with address dst.  Call the
323167612Ssimon * protocol specific routine to handle each connection.
324167612Ssimon * Also pass an extra paramter via the ipxpcb. (which may in fact
325167612Ssimon * be a parameter list!)
326167612Ssimon */
327167612Ssimonvoid
328167612Ssimonipx_pcbnotify(dst, errno, notify, param)
329167612Ssimon	register struct ipx_addr *dst;
330167612Ssimon	int errno;
331167612Ssimon	void (*notify)(struct ipxpcb *);
332167612Ssimon	long param;
333167612Ssimon{
334167612Ssimon	register struct ipxpcb *ipxp, *oinp;
335167612Ssimon	int s = splimp();
336167612Ssimon
337167612Ssimon	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb);) {
338167612Ssimon		if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) {
339167612Ssimon	next:
340167612Ssimon			ipxp = ipxp->ipxp_next;
341167612Ssimon			continue;
342167612Ssimon		}
343167612Ssimon		if (ipxp->ipxp_socket == 0)
344167612Ssimon			goto next;
345167612Ssimon		if (errno)
346167612Ssimon			ipxp->ipxp_socket->so_error = errno;
347167612Ssimon		oinp = ipxp;
348167612Ssimon		ipxp = ipxp->ipxp_next;
349167612Ssimon		oinp->ipxp_notify_param = param;
350167612Ssimon		(*notify)(oinp);
351167612Ssimon	}
352167612Ssimon	splx(s);
353167612Ssimon}
354167612Ssimon
355167612Ssimon#ifdef notdef
356167612Ssimon/*
357167612Ssimon * After a routing change, flush old routing
358167612Ssimon * and allocate a (hopefully) better one.
359167612Ssimon */
360167612Ssimonipx_rtchange(ipxp)
361167612Ssimon	struct ipxpcb *ipxp;
362167612Ssimon{
363167612Ssimon	if (ipxp->ipxp_route.ro_rt != NULL) {
364167612Ssimon		rtfree(ipxp->ipxp_route.ro_rt);
365167612Ssimon		ipxp->ipxp_route.ro_rt = NULL;
366167612Ssimon		/*
367167612Ssimon		 * A new route can be allocated the next time
368167612Ssimon		 * output is attempted.
369167612Ssimon		 */
370167612Ssimon	}
371167612Ssimon	/* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
372167612Ssimon}
373167612Ssimon#endif
374167612Ssimon
375167612Ssimonstruct ipxpcb *
376167612Ssimonipx_pcblookup(faddr, lport, wildp)
377167612Ssimon	struct ipx_addr *faddr;
378167612Ssimon	u_short lport;
379167612Ssimon	int wildp;
380167612Ssimon{
381167612Ssimon	register struct ipxpcb *ipxp, *match = 0;
382167612Ssimon	int matchwild = 3, wildcard;
383167612Ssimon	u_short fport;
384167612Ssimon
385167612Ssimon	fport = faddr->x_port;
386167612Ssimon	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb); ipxp = ipxp->ipxp_next) {
387167612Ssimon		if (ipxp->ipxp_lport != lport)
388167612Ssimon			continue;
389167612Ssimon		wildcard = 0;
390167612Ssimon		if (ipx_nullhost(ipxp->ipxp_faddr)) {
391167612Ssimon			if (!ipx_nullhost(*faddr))
392167612Ssimon				wildcard++;
393167612Ssimon		} else {
394167612Ssimon			if (ipx_nullhost(*faddr))
395167612Ssimon				wildcard++;
396167612Ssimon			else {
397167612Ssimon				if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr))
398167612Ssimon					continue;
399167612Ssimon				if (ipxp->ipxp_fport != fport) {
400167612Ssimon					if (ipxp->ipxp_fport != 0)
401167612Ssimon						continue;
402167612Ssimon					else
403167612Ssimon						wildcard++;
404167612Ssimon				}
405167612Ssimon			}
406167612Ssimon		}
407167612Ssimon		if (wildcard && wildp == 0)
408167612Ssimon			continue;
409167612Ssimon		if (wildcard < matchwild) {
410167612Ssimon			match = ipxp;
411167612Ssimon			matchwild = wildcard;
412167612Ssimon			if (wildcard == 0)
413167612Ssimon				break;
414167612Ssimon		}
415167612Ssimon	}
416167612Ssimon	return (match);
417167612Ssimon}
418167612Ssimon