ipx_pcb.c revision 169462
1/*-
2 * Copyright (c) 1984, 1985, 1986, 1987, 1993
3 *	The Regents of the University of California.
4 * Copyright (c) 2004-2006 Robert N. M. Watson
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 4. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * Copyright (c) 1995, Mike Mitchell
32 * All rights reserved.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
36 * are met:
37 * 1. Redistributions of source code must retain the above copyright
38 *    notice, this list of conditions and the following disclaimer.
39 * 2. Redistributions in binary form must reproduce the above copyright
40 *    notice, this list of conditions and the following disclaimer in the
41 *    documentation and/or other materials provided with the distribution.
42 * 3. All advertising materials mentioning features or use of this software
43 *    must display the following acknowledgement:
44 *	This product includes software developed by the University of
45 *	California, Berkeley and its contributors.
46 * 4. Neither the name of the University nor the names of its contributors
47 *    may be used to endorse or promote products derived from this software
48 *    without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * SUCH DAMAGE.
61 *
62 *	@(#)ipx_pcb.c
63 */
64
65#include <sys/cdefs.h>
66__FBSDID("$FreeBSD: head/sys/netipx/ipx_pcb.c 169462 2007-05-11 10:20:51Z rwatson $");
67
68#include <sys/param.h>
69#include <sys/systm.h>
70#include <sys/malloc.h>
71#include <sys/priv.h>
72#include <sys/socket.h>
73#include <sys/socketvar.h>
74
75#include <net/if.h>
76#include <net/route.h>
77
78#include <netipx/ipx.h>
79#include <netipx/ipx_if.h>
80#include <netipx/ipx_pcb.h>
81#include <netipx/ipx_var.h>
82
83static struct	ipx_addr zeroipx_addr;
84static u_short	ipxpcb_lport_cache;
85
86int
87ipx_pcballoc(so, head, td)
88	struct socket *so;
89	struct ipxpcbhead *head;
90	struct thread *td;
91{
92	register struct ipxpcb *ipxp;
93
94	KASSERT(so->so_pcb == NULL, ("ipx_pcballoc: so_pcb != NULL"));
95	IPX_LIST_LOCK_ASSERT();
96
97	MALLOC(ipxp, struct ipxpcb *, sizeof *ipxp, M_PCB, M_NOWAIT | M_ZERO);
98	if (ipxp == NULL)
99		return (ENOBUFS);
100	IPX_LOCK_INIT(ipxp);
101	ipxp->ipxp_socket = so;
102	if (ipxcksum)
103		ipxp->ipxp_flags |= IPXP_CHECKSUM;
104	LIST_INSERT_HEAD(head, ipxp, ipxp_list);
105	so->so_pcb = (caddr_t)ipxp;
106	return (0);
107}
108
109int
110ipx_pcbbind(ipxp, nam, td)
111	register struct ipxpcb *ipxp;
112	struct sockaddr *nam;
113	struct thread *td;
114{
115	register struct sockaddr_ipx *sipx;
116	u_short lport = 0;
117
118	IPX_LIST_LOCK_ASSERT();
119	IPX_LOCK_ASSERT(ipxp);
120
121	if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr))
122		return (EINVAL);
123	if (nam == NULL)
124		goto noname;
125	sipx = (struct sockaddr_ipx *)nam;
126	if (!ipx_nullhost(sipx->sipx_addr)) {
127		int tport = sipx->sipx_port;
128
129		sipx->sipx_port = 0;		/* yech... */
130		if (ifa_ifwithaddr((struct sockaddr *)sipx) == NULL)
131			return (EADDRNOTAVAIL);
132		sipx->sipx_port = tport;
133	}
134	lport = sipx->sipx_port;
135	if (lport) {
136		u_short aport = ntohs(lport);
137
138		if (aport < IPXPORT_RESERVED && td != NULL &&
139		    priv_check(td, PRIV_NETIPX_RESERVEDPORT))
140			return (EACCES);
141		if (ipx_pcblookup(&zeroipx_addr, lport, 0))
142			return (EADDRINUSE);
143	}
144	ipxp->ipxp_laddr = sipx->sipx_addr;
145noname:
146	if (lport == 0)
147		do {
148			ipxpcb_lport_cache++;
149			if ((ipxpcb_lport_cache < IPXPORT_RESERVED) ||
150			    (ipxpcb_lport_cache >= IPXPORT_WELLKNOWN))
151				ipxpcb_lport_cache = IPXPORT_RESERVED;
152			lport = htons(ipxpcb_lport_cache);
153		} while (ipx_pcblookup(&zeroipx_addr, lport, 0));
154	ipxp->ipxp_lport = lport;
155	return (0);
156}
157
158/*
159 * Connect from a socket to a specified address.
160 * Both address and port must be specified in argument sipx.
161 * If don't have a local address for this socket yet,
162 * then pick one.
163 */
164int
165ipx_pcbconnect(ipxp, nam, td)
166	struct ipxpcb *ipxp;
167	struct sockaddr *nam;
168	struct thread *td;
169{
170	struct ipx_ifaddr *ia;
171	register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)nam;
172	register struct ipx_addr *dst;
173	register struct route *ro;
174	struct ifnet *ifp;
175
176	IPX_LIST_LOCK_ASSERT();
177	IPX_LOCK_ASSERT(ipxp);
178
179	ia = NULL;
180
181	if (sipx->sipx_family != AF_IPX)
182		return (EAFNOSUPPORT);
183	if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr))
184		return (EADDRNOTAVAIL);
185	/*
186	 * If we haven't bound which network number to use as ours,
187	 * we will use the number of the outgoing interface.
188	 * This depends on having done a routing lookup, which
189	 * we will probably have to do anyway, so we might
190	 * as well do it now.  On the other hand if we are
191	 * sending to multiple destinations we may have already
192	 * done the lookup, so see if we can use the route
193	 * from before.  In any case, we only
194	 * chose a port number once, even if sending to multiple
195	 * destinations.
196	 */
197	ro = &ipxp->ipxp_route;
198	dst = &satoipx_addr(ro->ro_dst);
199	if (ipxp->ipxp_socket->so_options & SO_DONTROUTE)
200		goto flush;
201	if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr))
202		goto flush;
203	if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) {
204		if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) {
205			/* can patch route to avoid rtalloc */
206			*dst = sipx->sipx_addr;
207		} else {
208	flush:
209			if (ro->ro_rt != NULL)
210				RTFREE(ro->ro_rt);
211			ro->ro_rt = NULL;
212		}
213	}/* else cached route is ok; do nothing */
214	ipxp->ipxp_lastdst = sipx->sipx_addr;
215	if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
216	    (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
217		    /* No route yet, so try to acquire one */
218		    ro->ro_dst.sa_family = AF_IPX;
219		    ro->ro_dst.sa_len = sizeof(ro->ro_dst);
220		    *dst = sipx->sipx_addr;
221		    dst->x_port = 0;
222		    rtalloc_ign(ro, 0);
223	}
224	if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) {
225		/*
226		 * If route is known or can be allocated now,
227		 * our src addr is taken from the i/f, else punt.
228		 */
229
230		/*
231		 * If we found a route, use the address
232		 * corresponding to the outgoing interface
233		 */
234		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
235			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
236				if (ia->ia_ifp == ifp)
237					break;
238		if (ia == NULL) {
239			u_short fport = sipx->sipx_addr.x_port;
240			sipx->sipx_addr.x_port = 0;
241			ia = (struct ipx_ifaddr *)
242				ifa_ifwithdstaddr((struct sockaddr *)sipx);
243			sipx->sipx_addr.x_port = fport;
244			if (ia == NULL)
245				ia = ipx_iaonnetof(&sipx->sipx_addr);
246			if (ia == NULL)
247				ia = ipx_ifaddr;
248			if (ia == NULL)
249				return (EADDRNOTAVAIL);
250		}
251		ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net;
252	}
253	if (ipx_nullhost(ipxp->ipxp_laddr)) {
254		/*
255		 * If route is known or can be allocated now,
256		 * our src addr is taken from the i/f, else punt.
257		 */
258
259		/*
260		 * If we found a route, use the address
261		 * corresponding to the outgoing interface
262		 */
263		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
264			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
265				if (ia->ia_ifp == ifp)
266					break;
267		if (ia == NULL) {
268			u_short fport = sipx->sipx_addr.x_port;
269			sipx->sipx_addr.x_port = 0;
270			ia = (struct ipx_ifaddr *)
271				ifa_ifwithdstaddr((struct sockaddr *)sipx);
272			sipx->sipx_addr.x_port = fport;
273			if (ia == NULL)
274				ia = ipx_iaonnetof(&sipx->sipx_addr);
275			if (ia == NULL)
276				ia = ipx_ifaddr;
277			if (ia == NULL)
278				return (EADDRNOTAVAIL);
279		}
280		ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host;
281	}
282	if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
283		return (EADDRINUSE);
284	if (ipxp->ipxp_lport == 0)
285		ipx_pcbbind(ipxp, (struct sockaddr *)NULL, td);
286
287	/* XXX just leave it zero if we can't find a route */
288
289	ipxp->ipxp_faddr = sipx->sipx_addr;
290	/* Includes ipxp->ipxp_fport = sipx->sipx_port; */
291	return (0);
292}
293
294void
295ipx_pcbdisconnect(ipxp)
296	struct ipxpcb *ipxp;
297{
298
299	IPX_LIST_LOCK_ASSERT();
300	IPX_LOCK_ASSERT(ipxp);
301
302	ipxp->ipxp_faddr = zeroipx_addr;
303}
304
305void
306ipx_pcbdetach(ipxp)
307	struct ipxpcb *ipxp;
308{
309	struct socket *so = ipxp->ipxp_socket;
310
311	IPX_LIST_LOCK_ASSERT();
312	IPX_LOCK_ASSERT(ipxp);
313
314	so->so_pcb = NULL;
315	ipxp->ipxp_socket = NULL;
316}
317
318void
319ipx_pcbfree(ipxp)
320	struct ipxpcb *ipxp;
321{
322
323	KASSERT(ipxp->ipxp_socket == NULL,
324	    ("ipx_pcbfree: ipxp_socket != NULL"));
325	IPX_LIST_LOCK_ASSERT();
326	IPX_LOCK_ASSERT(ipxp);
327
328	if (ipxp->ipxp_route.ro_rt != NULL)
329		RTFREE(ipxp->ipxp_route.ro_rt);
330	LIST_REMOVE(ipxp, ipxp_list);
331	IPX_LOCK_DESTROY(ipxp);
332	FREE(ipxp, M_PCB);
333}
334
335void
336ipx_getsockaddr(ipxp, nam)
337	register struct ipxpcb *ipxp;
338	struct sockaddr **nam;
339{
340	struct sockaddr_ipx *sipx, ssipx;
341
342	sipx = &ssipx;
343	bzero((caddr_t)sipx, sizeof(*sipx));
344	sipx->sipx_len = sizeof(*sipx);
345	sipx->sipx_family = AF_IPX;
346	IPX_LOCK(ipxp);
347	sipx->sipx_addr = ipxp->ipxp_laddr;
348	IPX_UNLOCK(ipxp);
349	*nam = sodupsockaddr((struct sockaddr *)sipx, M_WAITOK);
350}
351
352void
353ipx_getpeeraddr(ipxp, nam)
354	register struct ipxpcb *ipxp;
355	struct sockaddr **nam;
356{
357	struct sockaddr_ipx *sipx, ssipx;
358
359	sipx = &ssipx;
360	bzero(sipx, sizeof(*sipx));
361	sipx->sipx_len = sizeof(*sipx);
362	sipx->sipx_family = AF_IPX;
363	IPX_LOCK(ipxp);
364	sipx->sipx_addr = ipxp->ipxp_faddr;
365	IPX_UNLOCK(ipxp);
366	*nam = sodupsockaddr((struct sockaddr *)sipx, M_WAITOK);
367}
368
369struct ipxpcb *
370ipx_pcblookup(faddr, lport, wildp)
371	struct ipx_addr *faddr;
372	u_short lport;
373	int wildp;
374{
375	register struct ipxpcb *ipxp, *match = NULL;
376	int matchwild = 3, wildcard;
377	u_short fport;
378
379	IPX_LIST_LOCK_ASSERT();
380
381	fport = faddr->x_port;
382	LIST_FOREACH(ipxp, &ipxpcb_list, ipxp_list) {
383		if (ipxp->ipxp_lport != lport)
384			continue;
385		wildcard = 0;
386		if (ipx_nullhost(ipxp->ipxp_faddr)) {
387			if (!ipx_nullhost(*faddr))
388				wildcard++;
389		} else {
390			if (ipx_nullhost(*faddr))
391				wildcard++;
392			else {
393				if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr))
394					continue;
395				if (ipxp->ipxp_fport != fport) {
396					if (ipxp->ipxp_fport != 0)
397						continue;
398					else
399						wildcard++;
400				}
401			}
402		}
403		if (wildcard && wildp == 0)
404			continue;
405		if (wildcard < matchwild) {
406			match = ipxp;
407			matchwild = wildcard;
408			if (wildcard == 0)
409				break;
410		}
411	}
412	return (match);
413}
414