ipx_pcb.c revision 33181
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.13 1997/10/28 15:58:56 bde 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			ipxp->ipxp_laddr.x_net = ipx_zeronet;
173		}
174	}/* else cached route is ok; do nothing */
175	ipxp->ipxp_lastdst = sipx->sipx_addr;
176	if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
177	    (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
178		    /* No route yet, so try to acquire one */
179		    ro->ro_dst.sa_family = AF_IPX;
180		    ro->ro_dst.sa_len = sizeof(ro->ro_dst);
181		    *dst = sipx->sipx_addr;
182		    dst->x_port = 0;
183		    rtalloc(ro);
184	}
185	if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) {
186		/*
187		 * If route is known or can be allocated now,
188		 * our src addr is taken from the i/f, else punt.
189		 */
190
191		/*
192		 * If we found a route, use the address
193		 * corresponding to the outgoing interface
194		 */
195		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
196			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
197				if (ia->ia_ifp == ifp)
198					break;
199		if (ia == NULL) {
200			u_short fport = sipx->sipx_addr.x_port;
201			sipx->sipx_addr.x_port = 0;
202			ia = (struct ipx_ifaddr *)
203				ifa_ifwithdstaddr((struct sockaddr *)sipx);
204			sipx->sipx_addr.x_port = fport;
205			if (ia == NULL)
206				ia = ipx_iaonnetof(&sipx->sipx_addr);
207			if (ia == NULL)
208				ia = ipx_ifaddr;
209			if (ia == NULL)
210				return (EADDRNOTAVAIL);
211		}
212		ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net;
213	}
214	if (ipx_nullhost(ipxp->ipxp_laddr)) {
215		/*
216		 * If route is known or can be allocated now,
217		 * our src addr is taken from the i/f, else punt.
218		 */
219
220		/*
221		 * If we found a route, use the address
222		 * corresponding to the outgoing interface
223		 */
224		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
225			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
226				if (ia->ia_ifp == ifp)
227					break;
228		if (ia == NULL) {
229			u_short fport = sipx->sipx_addr.x_port;
230			sipx->sipx_addr.x_port = 0;
231			ia = (struct ipx_ifaddr *)
232				ifa_ifwithdstaddr((struct sockaddr *)sipx);
233			sipx->sipx_addr.x_port = fport;
234			if (ia == NULL)
235				ia = ipx_iaonnetof(&sipx->sipx_addr);
236			if (ia == NULL)
237				ia = ipx_ifaddr;
238			if (ia == NULL)
239				return (EADDRNOTAVAIL);
240		}
241		ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host;
242	}
243	if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
244		return (EADDRINUSE);
245	if (ipxp->ipxp_lport == 0)
246		ipx_pcbbind(ipxp, (struct sockaddr *)NULL, p);
247
248	/* XXX just leave it zero if we can't find a route */
249
250	ipxp->ipxp_faddr = sipx->sipx_addr;
251	/* Includes ipxp->ipxp_fport = sipx->sipx_port; */
252	return (0);
253}
254
255void
256ipx_pcbdisconnect(ipxp)
257	struct ipxpcb *ipxp;
258{
259
260	ipxp->ipxp_faddr = zeroipx_addr;
261	if (ipxp->ipxp_socket->so_state & SS_NOFDREF)
262		ipx_pcbdetach(ipxp);
263}
264
265void
266ipx_pcbdetach(ipxp)
267	struct ipxpcb *ipxp;
268{
269	struct socket *so = ipxp->ipxp_socket;
270
271	so->so_pcb = 0;
272	sofree(so);
273	if (ipxp->ipxp_route.ro_rt != NULL)
274		rtfree(ipxp->ipxp_route.ro_rt);
275	remque(ipxp);
276	FREE(ipxp, M_PCB);
277}
278
279void
280ipx_setsockaddr(ipxp, nam)
281	register struct ipxpcb *ipxp;
282	struct sockaddr **nam;
283{
284	struct sockaddr_ipx *sipx, ssipx;
285
286	sipx = &ssipx;
287	bzero((caddr_t)sipx, sizeof(*sipx));
288	sipx->sipx_len = sizeof(*sipx);
289	sipx->sipx_family = AF_IPX;
290	sipx->sipx_addr = ipxp->ipxp_laddr;
291	*nam = dup_sockaddr((struct sockaddr *)sipx, 0);
292}
293
294void
295ipx_setpeeraddr(ipxp, nam)
296	register struct ipxpcb *ipxp;
297	struct sockaddr **nam;
298{
299	struct sockaddr_ipx *sipx, ssipx;
300
301	sipx = &ssipx;
302	bzero((caddr_t)sipx, sizeof(*sipx));
303	sipx->sipx_len = sizeof(*sipx);
304	sipx->sipx_family = AF_IPX;
305	sipx->sipx_addr = ipxp->ipxp_faddr;
306	*nam = dup_sockaddr((struct sockaddr *)sipx, 0);
307}
308
309/*
310 * Pass some notification to all connections of a protocol
311 * associated with address dst.  Call the
312 * protocol specific routine to handle each connection.
313 * Also pass an extra paramter via the ipxpcb. (which may in fact
314 * be a parameter list!)
315 */
316void
317ipx_pcbnotify(dst, errno, notify, param)
318	register struct ipx_addr *dst;
319	int errno;
320	void (*notify)(struct ipxpcb *);
321	long param;
322{
323	register struct ipxpcb *ipxp, *oinp;
324	int s = splimp();
325
326	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb);) {
327		if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) {
328	next:
329			ipxp = ipxp->ipxp_next;
330			continue;
331		}
332		if (ipxp->ipxp_socket == 0)
333			goto next;
334		if (errno)
335			ipxp->ipxp_socket->so_error = errno;
336		oinp = ipxp;
337		ipxp = ipxp->ipxp_next;
338		oinp->ipxp_notify_param = param;
339		(*notify)(oinp);
340	}
341	splx(s);
342}
343
344#ifdef notdef
345/*
346 * After a routing change, flush old routing
347 * and allocate a (hopefully) better one.
348 */
349ipx_rtchange(ipxp)
350	struct ipxpcb *ipxp;
351{
352	if (ipxp->ipxp_route.ro_rt != NULL) {
353		rtfree(ipxp->ipxp_route.ro_rt);
354		ipxp->ipxp_route.ro_rt = NULL;
355		/*
356		 * A new route can be allocated the next time
357		 * output is attempted.
358		 */
359	}
360	/* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
361}
362#endif
363
364struct ipxpcb *
365ipx_pcblookup(faddr, lport, wildp)
366	struct ipx_addr *faddr;
367	u_short lport;
368	int wildp;
369{
370	register struct ipxpcb *ipxp, *match = 0;
371	int matchwild = 3, wildcard;
372	u_short fport;
373
374	fport = faddr->x_port;
375	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb); ipxp = ipxp->ipxp_next) {
376		if (ipxp->ipxp_lport != lport)
377			continue;
378		wildcard = 0;
379		if (ipx_nullhost(ipxp->ipxp_faddr)) {
380			if (!ipx_nullhost(*faddr))
381				wildcard++;
382		} else {
383			if (ipx_nullhost(*faddr))
384				wildcard++;
385			else {
386				if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr))
387					continue;
388				if (ipxp->ipxp_fport != fport) {
389					if (ipxp->ipxp_fport != 0)
390						continue;
391					else
392						wildcard++;
393				}
394			}
395		}
396		if (wildcard && wildp == 0)
397			continue;
398		if (wildcard < matchwild) {
399			match = ipxp;
400			matchwild = wildcard;
401			if (wildcard == 0)
402				break;
403		}
404	}
405	return (match);
406}
407