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