1 /*	$OpenBSD: ipx_pcb.c,v 1.10 2003/12/10 07:22:43 itojun Exp $	*/
2 
3 /*-
4  *
5  * Copyright (c) 1996 Michael Shalayeff
6  * Copyright (c) 1995, Mike Mitchell
7  * Copyright (c) 1984, 1985, 1986, 1987, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. 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  * from FreeBSD Id: ipx_pcb.c,v 1.5 1996/03/11 15:13:53 davidg Exp
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/mbuf.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 
45 #include <net/if.h>
46 #include <net/route.h>
47 
48 #include <netipx/ipx.h>
49 #include <netipx/ipx_if.h>
50 #include <netipx/ipx_pcb.h>
51 
52 struct	ipx_addr zeroipx_addr;
53 
54 #define	IPXPCBHASH(table, faddr, fport, laddr, lport) \
55 	&(table)->ipxpt_hashtbl[(ntohl((faddr)->ipx_net.l_net) + \
56 	ntohs((fport)) + ntohs((lport))) & (table->ipxpt_hash)]
57 
58 void
ipx_pcbinit(table,hashsize)59 ipx_pcbinit(table, hashsize)
60 	struct ipxpcbtable *table;
61 	int hashsize;
62 {
63 	CIRCLEQ_INIT(&table->ipxpt_queue);
64 	table->ipxpt_hashtbl =
65 	    hashinit(hashsize, M_PCB, M_WAITOK, &table->ipxpt_hash);
66 	table->ipxpt_lport = 0;
67 }
68 
69 int
ipx_pcballoc(so,head)70 ipx_pcballoc(so, head)
71 	struct socket *so;
72 	struct ipxpcbtable *head;
73 {
74 	struct ipxpcb *ipxp;
75 	int s;
76 
77 	ipxp = malloc(sizeof(*ipxp), M_PCB, M_DONTWAIT);
78 	if (ipxp == NULL)
79 		return (ENOBUFS);
80 	bzero((caddr_t)ipxp, sizeof(*ipxp));
81 	ipxp->ipxp_socket = so;
82 	ipxp->ipxp_table = head;
83 	s = splnet();
84 	CIRCLEQ_INSERT_HEAD(&head->ipxpt_queue, ipxp, ipxp_queue);
85 	LIST_INSERT_HEAD(IPXPCBHASH(head, &ipxp->ipxp_faddr, ipxp->ipxp_fport,
86 	    &ipxp->ipxp_laddr, ipxp->ipxp_lport), ipxp, ipxp_hash);
87 	splx(s);
88 	so->so_pcb = (caddr_t)ipxp;
89 	return (0);
90 }
91 
92 int
ipx_pcbbind(ipxp,nam)93 ipx_pcbbind(ipxp, nam)
94 	struct ipxpcb *ipxp;
95 	struct mbuf *nam;
96 {
97 	struct sockaddr_ipx *sipx;
98 	u_short lport = 0;
99 
100 	if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr))
101 		return (EINVAL);
102 	if (nam == 0)
103 		goto noname;
104 	sipx = mtod(nam, struct sockaddr_ipx *);
105 	if (nam->m_len != sizeof(*sipx))
106 		return (EINVAL);
107 	if (!ipx_nullhost(sipx->sipx_addr)) {
108 		int tport = sipx->sipx_port;
109 
110 		sipx->sipx_port = 0;		/* yech... */
111 		if (ifa_ifwithaddr((struct sockaddr *)sipx) == 0)
112 			return (EADDRNOTAVAIL);
113 		sipx->sipx_port = tport;
114 	}
115 	lport = sipx->sipx_port;
116 	if (lport) {
117 		u_short aport = ntohs(lport);
118 
119 		if (aport < IPXPORT_RESERVED &&
120 		    (ipxp->ipxp_socket->so_state & SS_PRIV) == 0)
121 			return (EACCES);
122 		if (ipx_pcblookup(&zeroipx_addr, lport, 0))
123 			return (EADDRINUSE);
124 	}
125 	ipxp->ipxp_laddr = sipx->sipx_addr;
126 noname:
127 	if (lport == 0)
128 		do {
129 			if ((ipxcbtable.ipxpt_lport++ < IPXPORT_RESERVED) ||
130 			    (ipxcbtable.ipxpt_lport >= IPXPORT_WELLKNOWN))
131 				ipxcbtable.ipxpt_lport = IPXPORT_RESERVED;
132 			lport = htons(ipxcbtable.ipxpt_lport);
133 		} while (ipx_pcblookup(&zeroipx_addr, lport, 0));
134 	ipxp->ipxp_lport = lport;
135 	return (0);
136 }
137 
138 /*
139  * Connect from a socket to a specified address.
140  * Both address and port must be specified in argument sipx.
141  * If don't have a local address for this socket yet,
142  * then pick one.
143  */
144 int
ipx_pcbconnect(ipxp,nam)145 ipx_pcbconnect(ipxp, nam)
146 	struct ipxpcb *ipxp;
147 	struct mbuf *nam;
148 {
149 	struct ipx_ifaddr *ia;
150 	struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
151 	struct ipx_addr *dst;
152 	struct route *ro;
153 	struct ifnet *ifp;
154 
155 	if (nam->m_len != sizeof(*sipx))
156 		return (EINVAL);
157 	if (sipx->sipx_family != AF_IPX) {
158 #ifdef	DEBUG
159 		printf("ipx_connect: af=%x\n", sipx->sipx_family);
160 #endif
161 		return (EAFNOSUPPORT);
162 	}
163 	if (sipx->sipx_port==0 || ipx_nullhost(sipx->sipx_addr))
164 		return (EADDRNOTAVAIL);
165 	/*
166 	 * If we haven't bound which network number to use as ours,
167 	 * we will use the number of the outgoing interface.
168 	 * This depends on having done a routing lookup, which
169 	 * we will probably have to do anyway, so we might
170 	 * as well do it now.  On the other hand if we are
171 	 * sending to multiple destinations we may have already
172 	 * done the lookup, so see if we can use the route
173 	 * from before.  In any case, we only
174 	 * chose a port number once, even if sending to multiple
175 	 * destinations.
176 	 */
177 	ro = &ipxp->ipxp_route;
178 	dst = &satoipx_addr(ro->ro_dst);
179 	if (ipxp->ipxp_socket->so_options & SO_DONTROUTE)
180 		goto flush;
181 	if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr))
182 		goto flush;
183 	if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) {
184 		if (ro->ro_rt && ! (ro->ro_rt->rt_flags & RTF_HOST)) {
185 			/* can patch route to avoid rtalloc */
186 			*dst = sipx->sipx_addr;
187 		} else {
188 	flush:
189 			if (ro->ro_rt)
190 				RTFREE(ro->ro_rt);
191 			ro->ro_rt = (struct rtentry *)0;
192 			ipxp->ipxp_laddr.ipx_net = ipx_zeronet;
193 		}
194 	}/* else cached route is ok; do nothing */
195 	ipxp->ipxp_lastdst = sipx->sipx_addr;
196 	if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
197 	    (ro->ro_rt == (struct rtentry *)0 ||
198 	     ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
199 		    /* No route yet, so try to acquire one */
200 		    ro->ro_dst.sa_family = AF_IPX;
201 		    ro->ro_dst.sa_len = sizeof(ro->ro_dst);
202 		    *dst = sipx->sipx_addr;
203 		    dst->ipx_port = 0;
204 		    rtalloc(ro);
205 	}
206 	if (ipx_neteqnn(ipxp->ipxp_laddr.ipx_net, ipx_zeronet)) {
207 		/*
208 		 * If route is known or can be allocated now,
209 		 * our src addr is taken from the i/f, else punt.
210 		 */
211 
212 		ia = (struct ipx_ifaddr *)0;
213 		/*
214 		 * If we found a route, use the address
215 		 * corresponding to the outgoing interface
216 		 */
217 		if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp))
218 			for (ia = ipx_ifaddr.tqh_first; ia;
219 			     ia = ia->ia_list.tqe_next)
220 				if (ia->ia_ifp == ifp)
221 					break;
222 		if (ia == NULL) {
223 			u_short fport = sipx->sipx_addr.ipx_port;
224 			sipx->sipx_addr.ipx_port = 0;
225 			ia = (struct ipx_ifaddr *)
226 				ifa_ifwithdstaddr((struct sockaddr *)sipx);
227 			sipx->sipx_addr.ipx_port = fport;
228 			if (ia == NULL)
229 				ia = ipx_iaonnetof(&sipx->sipx_addr);
230 			if (ia == NULL)
231 				ia = ipx_ifaddr.tqh_first;
232 			if (ia == 0)
233 				return (EADDRNOTAVAIL);
234 		}
235 		ipxp->ipxp_laddr.ipx_net = satoipx_addr(ia->ia_addr).ipx_net;
236 	}
237 	if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
238 		return (EADDRINUSE);
239 	if (ipxp->ipxp_lport == 0)
240 		(void) ipx_pcbbind(ipxp, (struct mbuf *)0);
241 
242 	ipxp->ipxp_faddr = sipx->sipx_addr;
243 	/* Includes ipxp->ipxp_fport = sipx->sipx_port; */
244 	return (0);
245 }
246 
247 void
ipx_pcbdisconnect(ipxp)248 ipx_pcbdisconnect(ipxp)
249 	struct ipxpcb *ipxp;
250 {
251 
252 	ipxp->ipxp_faddr = zeroipx_addr;
253 	if (ipxp->ipxp_socket->so_state & SS_NOFDREF)
254 		ipx_pcbdetach(ipxp);
255 }
256 
257 void
ipx_pcbdetach(ipxp)258 ipx_pcbdetach(ipxp)
259 	struct ipxpcb *ipxp;
260 {
261 	struct socket *so = ipxp->ipxp_socket;
262 	int	s;
263 
264 	so->so_pcb = 0;
265 	sofree(so);
266 	if (ipxp->ipxp_route.ro_rt)
267 		rtfree(ipxp->ipxp_route.ro_rt);
268 	s = splnet();
269 	LIST_REMOVE(ipxp, ipxp_hash);
270 	CIRCLEQ_REMOVE(&ipxp->ipxp_table->ipxpt_queue, ipxp, ipxp_queue);
271 	splx(s);
272 	FREE(ipxp, M_PCB);
273 }
274 
275 void
ipx_setsockaddr(ipxp,nam)276 ipx_setsockaddr(ipxp, nam)
277 	struct ipxpcb *ipxp;
278 	struct mbuf *nam;
279 {
280 	struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
281 
282 	nam->m_len = sizeof(*sipx);
283 	sipx = mtod(nam, struct sockaddr_ipx *);
284 	bzero((caddr_t)sipx, sizeof(*sipx));
285 	sipx->sipx_len = sizeof(*sipx);
286 	sipx->sipx_family = AF_IPX;
287 	sipx->sipx_addr = ipxp->ipxp_laddr;
288 }
289 
290 void
ipx_setpeeraddr(ipxp,nam)291 ipx_setpeeraddr(ipxp, nam)
292 	struct ipxpcb *ipxp;
293 	struct mbuf *nam;
294 {
295 	struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
296 
297 	nam->m_len = sizeof(*sipx);
298 	sipx = mtod(nam, struct sockaddr_ipx *);
299 	bzero((caddr_t)sipx, sizeof(*sipx));
300 	sipx->sipx_len = sizeof(*sipx);
301 	sipx->sipx_family = AF_IPX;
302 	sipx->sipx_addr = ipxp->ipxp_faddr;
303 }
304 
305 /*
306  * Pass some notification to all connections of a protocol
307  * associated with address dst.  Call the
308  * protocol specific routine to handle each connection.
309  * Also pass an extra paramter via the ipxpcb. (which may in fact
310  * be a parameter list!)
311  */
312 void
ipx_pcbnotify(dst,errno,notify,param)313 ipx_pcbnotify(dst, errno, notify, param)
314 	struct ipx_addr *dst;
315 	int errno;
316 	void (*notify)(struct ipxpcb *);
317 	long param;
318 {
319 	struct ipxpcb *ipxp, *oinp;
320 	int s = splimp();
321 
322 	for (ipxp = ipxcbtable.ipxpt_queue.cqh_first;
323 	    ipxp != (struct ipxpcb *)&ipxcbtable.ipxpt_queue;) {
324 		if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) {
325 	next:
326 			ipxp = ipxp->ipxp_queue.cqe_next;
327 			continue;
328 		}
329 		if (ipxp->ipxp_socket == 0)
330 			goto next;
331 		if (errno)
332 			ipxp->ipxp_socket->so_error = errno;
333 		oinp = ipxp;
334 		ipxp = ipxp->ipxp_queue.cqe_next;
335 		oinp->ipxp_notify_param = param;
336 		(*notify)(oinp);
337 	}
338 	splx(s);
339 }
340 
341 #ifdef notdef
342 /*
343  * After a routing change, flush old routing
344  * and allocate a (hopefully) better one.
345  */
346 ipx_rtchange(ipxp)
347 	struct ipxpcb *ipxp;
348 {
349 	if (ipxp->ipxp_route.ro_rt) {
350 		rtfree(ipxp->ipxp_route.ro_rt);
351 		ipxp->ipxp_route.ro_rt = 0;
352 		/*
353 		 * A new route can be allocated the next time
354 		 * output is attempted.
355 		 */
356 	}
357 	/* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
358 }
359 #endif
360 
361 struct ipxpcb *
ipx_pcblookup(faddr,lport,wildp)362 ipx_pcblookup(faddr, lport, wildp)
363 	struct ipx_addr *faddr;
364 	u_short lport;
365 	int wildp;
366 {
367 	struct ipxpcb *ipxp, *match = 0;
368 	int matchwild = 3, wildcard;
369 	u_short fport;
370 
371 	fport = faddr->ipx_port;
372 	for (ipxp = ipxcbtable.ipxpt_queue.cqh_first;
373 	    ipxp != (struct ipxpcb *)&ipxcbtable.ipxpt_queue;
374 	    ipxp = ipxp->ipxp_queue.cqe_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