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