ipx_pcb.c revision 43712
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_pcb.c
35 *
36 * $Id: ipx_pcb.c,v 1.14 1998/02/09 06:10:23 eivind Exp $
37 */
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/malloc.h>
42#include <sys/proc.h>
43#include <sys/socket.h>
44#include <sys/socketvar.h>
45
46#include <net/if.h>
47#include <net/route.h>
48
49#include <netipx/ipx.h>
50#include <netipx/ipx_if.h>
51#include <netipx/ipx_pcb.h>
52#include <netipx/ipx_var.h>
53
54static struct	ipx_addr zeroipx_addr;
55
56int
57ipx_pcballoc(so, head, p)
58	struct socket *so;
59	struct ipxpcb *head;
60	struct proc *p;
61{
62	register struct ipxpcb *ipxp;
63
64	MALLOC(ipxp, struct ipxpcb *, sizeof *ipxp, M_PCB, M_NOWAIT);
65	if (ipxp == NULL)
66		return (ENOBUFS);
67	bzero(ipxp, sizeof *ipxp);
68	ipxp->ipxp_socket = so;
69	insque(ipxp, head);
70	so->so_pcb = (caddr_t)ipxp;
71	return (0);
72}
73
74int
75ipx_pcbbind(ipxp, nam, p)
76	register struct ipxpcb *ipxp;
77	struct sockaddr *nam;
78	struct proc *p;
79{
80	register struct sockaddr_ipx *sipx;
81	u_short lport = 0;
82
83	if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr))
84		return (EINVAL);
85	if (nam == NULL)
86		goto noname;
87	sipx = (struct sockaddr_ipx *)nam;
88	if (!ipx_nullhost(sipx->sipx_addr)) {
89		int tport = sipx->sipx_port;
90
91		sipx->sipx_port = 0;		/* yech... */
92		if (ifa_ifwithaddr((struct sockaddr *)sipx) == 0)
93			return (EADDRNOTAVAIL);
94		sipx->sipx_port = tport;
95	}
96	lport = sipx->sipx_port;
97	if (lport) {
98		u_short aport = ntohs(lport);
99		int error;
100
101		if (aport < IPXPORT_RESERVED &&
102		    p != NULL && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
103			return (error);
104		if (ipx_pcblookup(&zeroipx_addr, lport, 0))
105			return (EADDRINUSE);
106	}
107	ipxp->ipxp_laddr = sipx->sipx_addr;
108noname:
109	if (lport == 0)
110		do {
111			ipxpcb.ipxp_lport++;
112			if ((ipxpcb.ipxp_lport < IPXPORT_RESERVED) ||
113			    (ipxpcb.ipxp_lport >= IPXPORT_WELLKNOWN))
114				ipxpcb.ipxp_lport = IPXPORT_RESERVED;
115			lport = htons(ipxpcb.ipxp_lport);
116		} while (ipx_pcblookup(&zeroipx_addr, lport, 0));
117	ipxp->ipxp_lport = lport;
118	return (0);
119}
120
121/*
122 * Connect from a socket to a specified address.
123 * Both address and port must be specified in argument sipx.
124 * If don't have a local address for this socket yet,
125 * then pick one.
126 */
127int
128ipx_pcbconnect(ipxp, nam, p)
129	struct ipxpcb *ipxp;
130	struct sockaddr *nam;
131	struct proc *p;
132{
133	struct ipx_ifaddr *ia;
134	register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)nam;
135	register struct ipx_addr *dst;
136	register struct route *ro;
137	struct ifnet *ifp;
138
139	ia = NULL;
140
141	if (sipx->sipx_family != AF_IPX)
142		return (EAFNOSUPPORT);
143	if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr))
144		return (EADDRNOTAVAIL);
145	/*
146	 * If we haven't bound which network number to use as ours,
147	 * we will use the number of the outgoing interface.
148	 * This depends on having done a routing lookup, which
149	 * we will probably have to do anyway, so we might
150	 * as well do it now.  On the other hand if we are
151	 * sending to multiple destinations we may have already
152	 * done the lookup, so see if we can use the route
153	 * from before.  In any case, we only
154	 * chose a port number once, even if sending to multiple
155	 * destinations.
156	 */
157	ro = &ipxp->ipxp_route;
158	dst = &satoipx_addr(ro->ro_dst);
159	if (ipxp->ipxp_socket->so_options & SO_DONTROUTE)
160		goto flush;
161	if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr))
162		goto flush;
163	if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) {
164		if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) {
165			/* can patch route to avoid rtalloc */
166			*dst = sipx->sipx_addr;
167		} else {
168	flush:
169			if (ro->ro_rt != NULL)
170				RTFREE(ro->ro_rt);
171			ro->ro_rt = NULL;
172		}
173	}/* else cached route is ok; do nothing */
174	ipxp->ipxp_lastdst = sipx->sipx_addr;
175	if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
176	    (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
177		    /* No route yet, so try to acquire one */
178		    ro->ro_dst.sa_family = AF_IPX;
179		    ro->ro_dst.sa_len = sizeof(ro->ro_dst);
180		    *dst = sipx->sipx_addr;
181		    dst->x_port = 0;
182		    rtalloc(ro);
183	}
184	if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) {
185		/*
186		 * If route is known or can be allocated now,
187		 * our src addr is taken from the i/f, else punt.
188		 */
189
190		/*
191		 * If we found a route, use the address
192		 * corresponding to the outgoing interface
193		 */
194		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
195			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
196				if (ia->ia_ifp == ifp)
197					break;
198		if (ia == NULL) {
199			u_short fport = sipx->sipx_addr.x_port;
200			sipx->sipx_addr.x_port = 0;
201			ia = (struct ipx_ifaddr *)
202				ifa_ifwithdstaddr((struct sockaddr *)sipx);
203			sipx->sipx_addr.x_port = fport;
204			if (ia == NULL)
205				ia = ipx_iaonnetof(&sipx->sipx_addr);
206			if (ia == NULL)
207				ia = ipx_ifaddr;
208			if (ia == NULL)
209				return (EADDRNOTAVAIL);
210		}
211		ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net;
212	}
213	if (ipx_nullhost(ipxp->ipxp_laddr)) {
214		/*
215		 * If route is known or can be allocated now,
216		 * our src addr is taken from the i/f, else punt.
217		 */
218
219		/*
220		 * If we found a route, use the address
221		 * corresponding to the outgoing interface
222		 */
223		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
224			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
225				if (ia->ia_ifp == ifp)
226					break;
227		if (ia == NULL) {
228			u_short fport = sipx->sipx_addr.x_port;
229			sipx->sipx_addr.x_port = 0;
230			ia = (struct ipx_ifaddr *)
231				ifa_ifwithdstaddr((struct sockaddr *)sipx);
232			sipx->sipx_addr.x_port = fport;
233			if (ia == NULL)
234				ia = ipx_iaonnetof(&sipx->sipx_addr);
235			if (ia == NULL)
236				ia = ipx_ifaddr;
237			if (ia == NULL)
238				return (EADDRNOTAVAIL);
239		}
240		ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host;
241	}
242	if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
243		return (EADDRINUSE);
244	if (ipxp->ipxp_lport == 0)
245		ipx_pcbbind(ipxp, (struct sockaddr *)NULL, p);
246
247	/* XXX just leave it zero if we can't find a route */
248
249	ipxp->ipxp_faddr = sipx->sipx_addr;
250	/* Includes ipxp->ipxp_fport = sipx->sipx_port; */
251	return (0);
252}
253
254void
255ipx_pcbdisconnect(ipxp)
256	struct ipxpcb *ipxp;
257{
258
259	ipxp->ipxp_faddr = zeroipx_addr;
260	if (ipxp->ipxp_socket->so_state & SS_NOFDREF)
261		ipx_pcbdetach(ipxp);
262}
263
264void
265ipx_pcbdetach(ipxp)
266	struct ipxpcb *ipxp;
267{
268	struct socket *so = ipxp->ipxp_socket;
269
270	so->so_pcb = 0;
271	sofree(so);
272	if (ipxp->ipxp_route.ro_rt != NULL)
273		rtfree(ipxp->ipxp_route.ro_rt);
274	remque(ipxp);
275	FREE(ipxp, M_PCB);
276}
277
278void
279ipx_setsockaddr(ipxp, nam)
280	register struct ipxpcb *ipxp;
281	struct sockaddr **nam;
282{
283	struct sockaddr_ipx *sipx, ssipx;
284
285	sipx = &ssipx;
286	bzero((caddr_t)sipx, sizeof(*sipx));
287	sipx->sipx_len = sizeof(*sipx);
288	sipx->sipx_family = AF_IPX;
289	sipx->sipx_addr = ipxp->ipxp_laddr;
290	*nam = dup_sockaddr((struct sockaddr *)sipx, 0);
291}
292
293void
294ipx_setpeeraddr(ipxp, nam)
295	register struct ipxpcb *ipxp;
296	struct sockaddr **nam;
297{
298	struct sockaddr_ipx *sipx, ssipx;
299
300	sipx = &ssipx;
301	bzero((caddr_t)sipx, sizeof(*sipx));
302	sipx->sipx_len = sizeof(*sipx);
303	sipx->sipx_family = AF_IPX;
304	sipx->sipx_addr = ipxp->ipxp_faddr;
305	*nam = dup_sockaddr((struct sockaddr *)sipx, 0);
306}
307
308/*
309 * Pass some notification to all connections of a protocol
310 * associated with address dst.  Call the
311 * protocol specific routine to handle each connection.
312 * Also pass an extra paramter via the ipxpcb. (which may in fact
313 * be a parameter list!)
314 */
315void
316ipx_pcbnotify(dst, errno, notify, param)
317	register struct ipx_addr *dst;
318	int errno;
319	void (*notify)(struct ipxpcb *);
320	long param;
321{
322	register struct ipxpcb *ipxp, *oinp;
323	int s = splimp();
324
325	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb);) {
326		if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) {
327	next:
328			ipxp = ipxp->ipxp_next;
329			continue;
330		}
331		if (ipxp->ipxp_socket == 0)
332			goto next;
333		if (errno)
334			ipxp->ipxp_socket->so_error = errno;
335		oinp = ipxp;
336		ipxp = ipxp->ipxp_next;
337		oinp->ipxp_notify_param = param;
338		(*notify)(oinp);
339	}
340	splx(s);
341}
342
343#ifdef notdef
344/*
345 * After a routing change, flush old routing
346 * and allocate a (hopefully) better one.
347 */
348ipx_rtchange(ipxp)
349	struct ipxpcb *ipxp;
350{
351	if (ipxp->ipxp_route.ro_rt != NULL) {
352		rtfree(ipxp->ipxp_route.ro_rt);
353		ipxp->ipxp_route.ro_rt = NULL;
354		/*
355		 * A new route can be allocated the next time
356		 * output is attempted.
357		 */
358	}
359	/* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
360}
361#endif
362
363struct ipxpcb *
364ipx_pcblookup(faddr, lport, wildp)
365	struct ipx_addr *faddr;
366	u_short lport;
367	int wildp;
368{
369	register struct ipxpcb *ipxp, *match = 0;
370	int matchwild = 3, wildcard;
371	u_short fport;
372
373	fport = faddr->x_port;
374	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb); ipxp = ipxp->ipxp_next) {
375		if (ipxp->ipxp_lport != lport)
376			continue;
377		wildcard = 0;
378		if (ipx_nullhost(ipxp->ipxp_faddr)) {
379			if (!ipx_nullhost(*faddr))
380				wildcard++;
381		} else {
382			if (ipx_nullhost(*faddr))
383				wildcard++;
384			else {
385				if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr))
386					continue;
387				if (ipxp->ipxp_fport != fport) {
388					if (ipxp->ipxp_fport != 0)
389						continue;
390					else
391						wildcard++;
392				}
393			}
394		}
395		if (wildcard && wildp == 0)
396			continue;
397		if (wildcard < matchwild) {
398			match = ipxp;
399			matchwild = wildcard;
400			if (wildcard == 0)
401				break;
402		}
403	}
404	return (match);
405}
406