ipx_pcb.c revision 25652
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.8 1997/05/01 06:21:29 jhay Exp $
37 */
38
39#include <sys/param.h>
40#include <sys/queue.h>
41#include <sys/systm.h>
42#include <sys/mbuf.h>
43#include <sys/proc.h>
44#include <sys/protosw.h>
45#include <sys/errno.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
57struct	ipx_addr zeroipx_addr;
58
59int
60ipx_pcballoc(so, head, p)
61	struct socket *so;
62	struct ipxpcb *head;
63	struct proc *p;
64{
65	struct mbuf *m;
66	register struct ipxpcb *ipxp;
67
68	m = m_getclr(M_DONTWAIT, MT_PCB);
69	if (m == NULL)
70		return (ENOBUFS);
71	ipxp = mtod(m, struct ipxpcb *);
72	ipxp->ipxp_socket = so;
73	insque(ipxp, head);
74	so->so_pcb = (caddr_t)ipxp;
75	return (0);
76}
77
78int
79ipx_pcbbind(ipxp, nam, p)
80	register struct ipxpcb *ipxp;
81	struct mbuf *nam;
82	struct proc *p;
83{
84	register struct sockaddr_ipx *sipx;
85	u_short lport = 0;
86
87	if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr))
88		return (EINVAL);
89	if (nam == NULL)
90		goto noname;
91	sipx = mtod(nam, struct sockaddr_ipx *);
92	if (nam->m_len != sizeof(*sipx))
93		return (EINVAL);
94	if (!ipx_nullhost(sipx->sipx_addr)) {
95		int tport = sipx->sipx_port;
96
97		sipx->sipx_port = 0;		/* yech... */
98		if (ifa_ifwithaddr((struct sockaddr *)sipx) == 0)
99			return (EADDRNOTAVAIL);
100		sipx->sipx_port = tport;
101	}
102	lport = sipx->sipx_port;
103	if (lport) {
104		u_short aport = ntohs(lport);
105		int error;
106
107		if (aport < IPXPORT_RESERVED &&
108		    p != NULL && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
109			return (error);
110		if (ipx_pcblookup(&zeroipx_addr, lport, 0))
111			return (EADDRINUSE);
112	}
113	ipxp->ipxp_laddr = sipx->sipx_addr;
114noname:
115	if (lport == 0)
116		do {
117			ipxpcb.ipxp_lport++;
118			if ((ipxpcb.ipxp_lport < IPXPORT_RESERVED) ||
119			    (ipxpcb.ipxp_lport >= IPXPORT_WELLKNOWN))
120				ipxpcb.ipxp_lport = IPXPORT_RESERVED;
121			lport = htons(ipxpcb.ipxp_lport);
122		} while (ipx_pcblookup(&zeroipx_addr, lport, 0));
123	ipxp->ipxp_lport = lport;
124	return (0);
125}
126
127/*
128 * Connect from a socket to a specified address.
129 * Both address and port must be specified in argument sipx.
130 * If don't have a local address for this socket yet,
131 * then pick one.
132 */
133int
134ipx_pcbconnect(ipxp, nam, p)
135	struct ipxpcb *ipxp;
136	struct mbuf *nam;
137	struct proc *p;
138{
139	struct ipx_ifaddr *ia;
140	register struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
141	register struct ipx_addr *dst;
142	register struct route *ro;
143	struct ifnet *ifp;
144
145	ia = NULL;
146
147	if (nam->m_len != sizeof(*sipx))
148		return (EINVAL);
149	if (sipx->sipx_family != AF_IPX)
150		return (EAFNOSUPPORT);
151	if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr))
152		return (EADDRNOTAVAIL);
153	/*
154	 * If we haven't bound which network number to use as ours,
155	 * we will use the number of the outgoing interface.
156	 * This depends on having done a routing lookup, which
157	 * we will probably have to do anyway, so we might
158	 * as well do it now.  On the other hand if we are
159	 * sending to multiple destinations we may have already
160	 * done the lookup, so see if we can use the route
161	 * from before.  In any case, we only
162	 * chose a port number once, even if sending to multiple
163	 * destinations.
164	 */
165	ro = &ipxp->ipxp_route;
166	dst = &satoipx_addr(ro->ro_dst);
167	if (ipxp->ipxp_socket->so_options & SO_DONTROUTE)
168		goto flush;
169	if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr))
170		goto flush;
171	if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) {
172		if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) {
173			/* can patch route to avoid rtalloc */
174			*dst = sipx->sipx_addr;
175		} else {
176	flush:
177			if (ro->ro_rt != NULL)
178				RTFREE(ro->ro_rt);
179			ro->ro_rt = NULL;
180			ipxp->ipxp_laddr.x_net = ipx_zeronet;
181		}
182	}/* else cached route is ok; do nothing */
183	ipxp->ipxp_lastdst = sipx->sipx_addr;
184	if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
185	    (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
186		    /* No route yet, so try to acquire one */
187		    ro->ro_dst.sa_family = AF_IPX;
188		    ro->ro_dst.sa_len = sizeof(ro->ro_dst);
189		    *dst = sipx->sipx_addr;
190		    dst->x_port = 0;
191		    rtalloc(ro);
192	}
193	if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) {
194		/*
195		 * If route is known or can be allocated now,
196		 * our src addr is taken from the i/f, else punt.
197		 */
198
199		/*
200		 * If we found a route, use the address
201		 * corresponding to the outgoing interface
202		 */
203		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
204			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
205				if (ia->ia_ifp == ifp)
206					break;
207		if (ia == NULL) {
208			u_short fport = sipx->sipx_addr.x_port;
209			sipx->sipx_addr.x_port = 0;
210			ia = (struct ipx_ifaddr *)
211				ifa_ifwithdstaddr((struct sockaddr *)sipx);
212			sipx->sipx_addr.x_port = fport;
213			if (ia == NULL)
214				ia = ipx_iaonnetof(&sipx->sipx_addr);
215			if (ia == NULL)
216				ia = ipx_ifaddr;
217			if (ia == NULL)
218				return (EADDRNOTAVAIL);
219		}
220		ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net;
221	}
222	if (ipx_nullhost(ipxp->ipxp_laddr)) {
223		/*
224		 * If route is known or can be allocated now,
225		 * our src addr is taken from the i/f, else punt.
226		 */
227
228		/*
229		 * If we found a route, use the address
230		 * corresponding to the outgoing interface
231		 */
232		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
233			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
234				if (ia->ia_ifp == ifp)
235					break;
236		if (ia == NULL) {
237			u_short fport = sipx->sipx_addr.x_port;
238			sipx->sipx_addr.x_port = 0;
239			ia = (struct ipx_ifaddr *)
240				ifa_ifwithdstaddr((struct sockaddr *)sipx);
241			sipx->sipx_addr.x_port = fport;
242			if (ia == NULL)
243				ia = ipx_iaonnetof(&sipx->sipx_addr);
244			if (ia == NULL)
245				ia = ipx_ifaddr;
246			if (ia == NULL)
247				return (EADDRNOTAVAIL);
248		}
249		ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host;
250	}
251	if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
252		return (EADDRINUSE);
253	if (ipxp->ipxp_lport == 0)
254		ipx_pcbbind(ipxp, (struct mbuf *)NULL, p);
255
256	/* XXX just leave it zero if we can't find a route */
257
258	ipxp->ipxp_faddr = sipx->sipx_addr;
259	/* Includes ipxp->ipxp_fport = sipx->sipx_port; */
260	return (0);
261}
262
263void
264ipx_pcbdisconnect(ipxp)
265	struct ipxpcb *ipxp;
266{
267
268	ipxp->ipxp_faddr = zeroipx_addr;
269	if (ipxp->ipxp_socket->so_state & SS_NOFDREF)
270		ipx_pcbdetach(ipxp);
271}
272
273void
274ipx_pcbdetach(ipxp)
275	struct ipxpcb *ipxp;
276{
277	struct socket *so = ipxp->ipxp_socket;
278
279	so->so_pcb = 0;
280	sofree(so);
281	if (ipxp->ipxp_route.ro_rt != NULL)
282		rtfree(ipxp->ipxp_route.ro_rt);
283	remque(ipxp);
284	m_free(dtom(ipxp));
285}
286
287void
288ipx_setsockaddr(ipxp, nam)
289	register struct ipxpcb *ipxp;
290	struct mbuf *nam;
291{
292	register struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
293
294	nam->m_len = sizeof(*sipx);
295	sipx = mtod(nam, struct sockaddr_ipx *);
296	bzero((caddr_t)sipx, sizeof(*sipx));
297	sipx->sipx_len = sizeof(*sipx);
298	sipx->sipx_family = AF_IPX;
299	sipx->sipx_addr = ipxp->ipxp_laddr;
300}
301
302void
303ipx_setpeeraddr(ipxp, nam)
304	register struct ipxpcb *ipxp;
305	struct mbuf *nam;
306{
307	register struct sockaddr_ipx *sipx = mtod(nam, struct sockaddr_ipx *);
308
309	nam->m_len = sizeof(*sipx);
310	sipx = mtod(nam, struct sockaddr_ipx *);
311	bzero((caddr_t)sipx, sizeof(*sipx));
312	sipx->sipx_len = sizeof(*sipx);
313	sipx->sipx_family = AF_IPX;
314	sipx->sipx_addr = ipxp->ipxp_faddr;
315}
316
317/*
318 * Pass some notification to all connections of a protocol
319 * associated with address dst.  Call the
320 * protocol specific routine to handle each connection.
321 * Also pass an extra paramter via the ipxpcb. (which may in fact
322 * be a parameter list!)
323 */
324void
325ipx_pcbnotify(dst, errno, notify, param)
326	register struct ipx_addr *dst;
327	int errno;
328	void (*notify)(struct ipxpcb *);
329	long param;
330{
331	register struct ipxpcb *ipxp, *oinp;
332	int s = splimp();
333
334	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb);) {
335		if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) {
336	next:
337			ipxp = ipxp->ipxp_next;
338			continue;
339		}
340		if (ipxp->ipxp_socket == 0)
341			goto next;
342		if (errno)
343			ipxp->ipxp_socket->so_error = errno;
344		oinp = ipxp;
345		ipxp = ipxp->ipxp_next;
346		oinp->ipxp_notify_param = param;
347		(*notify)(oinp);
348	}
349	splx(s);
350}
351
352#ifdef notdef
353/*
354 * After a routing change, flush old routing
355 * and allocate a (hopefully) better one.
356 */
357ipx_rtchange(ipxp)
358	struct ipxpcb *ipxp;
359{
360	if (ipxp->ipxp_route.ro_rt != NULL) {
361		rtfree(ipxp->ipxp_route.ro_rt);
362		ipxp->ipxp_route.ro_rt = NULL;
363		/*
364		 * A new route can be allocated the next time
365		 * output is attempted.
366		 */
367	}
368	/* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
369}
370#endif
371
372struct ipxpcb *
373ipx_pcblookup(faddr, lport, wildp)
374	struct ipx_addr *faddr;
375	u_short lport;
376	int wildp;
377{
378	register struct ipxpcb *ipxp, *match = 0;
379	int matchwild = 3, wildcard;
380	u_short fport;
381
382	fport = faddr->x_port;
383	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb); ipxp = ipxp->ipxp_next) {
384		if (ipxp->ipxp_lport != lport)
385			continue;
386		wildcard = 0;
387		if (ipx_nullhost(ipxp->ipxp_faddr)) {
388			if (!ipx_nullhost(*faddr))
389				wildcard++;
390		} else {
391			if (ipx_nullhost(*faddr))
392				wildcard++;
393			else {
394				if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr))
395					continue;
396				if (ipxp->ipxp_fport != fport) {
397					if (ipxp->ipxp_fport != 0)
398						continue;
399					else
400						wildcard++;
401				}
402			}
403		}
404		if (wildcard && wildp == 0)
405			continue;
406		if (wildcard < matchwild) {
407			match = ipxp;
408			matchwild = wildcard;
409			if (wildcard == 0)
410				break;
411		}
412	}
413	return (match);
414}
415