ipx_pcb.c revision 30813
13070Spst/*
23070Spst * Copyright (c) 1995, Mike Mitchell
33070Spst * Copyright (c) 1984, 1985, 1986, 1987, 1993
43070Spst *	The Regents of the University of California.  All rights reserved.
53070Spst *
63070Spst * Redistribution and use in source and binary forms, with or without
73070Spst * modification, are permitted provided that the following conditions
83070Spst * are met:
93070Spst * 1. Redistributions of source code must retain the above copyright
103070Spst *    notice, this list of conditions and the following disclaimer.
113070Spst * 2. Redistributions in binary form must reproduce the above copyright
123070Spst *    notice, this list of conditions and the following disclaimer in the
133070Spst *    documentation and/or other materials provided with the distribution.
143070Spst * 3. All advertising materials mentioning features or use of this software
153070Spst *    must display the following acknowledgement:
163070Spst *	This product includes software developed by the University of
173070Spst *	California, Berkeley and its contributors.
183070Spst * 4. Neither the name of the University nor the names of its contributors
193070Spst *    may be used to endorse or promote products derived from this software
203070Spst *    without specific prior written permission.
213070Spst *
223070Spst * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
233070Spst * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
243070Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
253070Spst * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2692986Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2792986Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
283070Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29113977Snectar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30145626Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
313070Spst * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323070Spst * SUCH DAMAGE.
333070Spst *
343070Spst *	@(#)ipx_pcb.c
353070Spst *
363070Spst * $Id: ipx_pcb.c,v 1.12 1997/09/02 01:19:10 bde Exp $
373070Spst */
38157779Sume
39145626Sume#include <sys/param.h>
403070Spst#include <sys/systm.h>
4165532Snectar#include <sys/malloc.h>
4265532Snectar#include <sys/proc.h>
43113977Snectar#include <sys/socket.h>
44145602Sume#include <sys/socketvar.h>
45158115Sume
46158115Sume#include <net/if.h>
47158115Sume#include <net/route.h>
483070Spst
4965532Snectar#include <netipx/ipx.h>
5065532Snectar#include <netipx/ipx_if.h>
5165532Snectar#include <netipx/ipx_pcb.h>
5265532Snectar#include <netipx/ipx_var.h>
5365532Snectar
5465532Snectarstruct	ipx_addr zeroipx_addr;
553070Spst
5665532Snectarint
57157779Sumeipx_pcballoc(so, head, p)
5865532Snectar	struct socket *so;
5965532Snectar	struct ipxpcb *head;
6065532Snectar	struct proc *p;
613070Spst{
623070Spst	register struct ipxpcb *ipxp;
63157779Sume
64157779Sume	MALLOC(ipxp, struct ipxpcb *, sizeof *ipxp, M_PCB, M_NOWAIT);
65145626Sume	if (ipxp == NULL)
66158115Sume		return (ENOBUFS);
67158115Sume	bzero(ipxp, sizeof *ipxp);
68158115Sume	ipxp->ipxp_socket = so;
69158115Sume	insque(ipxp, head);
70158115Sume	so->so_pcb = (caddr_t)ipxp;
71158115Sume	return (0);
72158115Sume}
73158115Sume
74158115Sumeint
75158115Sumeipx_pcbbind(ipxp, nam, p)
76158115Sume	register struct ipxpcb *ipxp;
77158115Sume	struct sockaddr *nam;
78158115Sume	struct proc *p;
79158115Sume{
80158115Sume	register struct sockaddr_ipx *sipx;
81158115Sume	u_short lport = 0;
82158115Sume
83158115Sume	if (ipxp->ipxp_lport || !ipx_nullhost(ipxp->ipxp_laddr))
84158115Sume		return (EINVAL);
85158115Sume	if (nam == NULL)
86158115Sume		goto noname;
87158115Sume	sipx = (struct sockaddr_ipx *)nam;
88158115Sume	if (!ipx_nullhost(sipx->sipx_addr)) {
89158115Sume		int tport = sipx->sipx_port;
90158115Sume
91158115Sume		sipx->sipx_port = 0;		/* yech... */
92158115Sume		if (ifa_ifwithaddr((struct sockaddr *)sipx) == 0)
93158115Sume			return (EADDRNOTAVAIL);
94158115Sume		sipx->sipx_port = tport;
95158115Sume	}
96158115Sume	lport = sipx->sipx_port;
97158115Sume	if (lport) {
98158115Sume		u_short aport = ntohs(lport);
99158115Sume		int error;
100158115Sume
101158115Sume		if (aport < IPXPORT_RESERVED &&
102158115Sume		    p != NULL && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
103158115Sume			return (error);
104158115Sume		if (ipx_pcblookup(&zeroipx_addr, lport, 0))
105158115Sume			return (EADDRINUSE);
106158115Sume	}
107158115Sume	ipxp->ipxp_laddr = sipx->sipx_addr;
108158115Sumenoname:
109158115Sume	if (lport == 0)
110158115Sume		do {
111158115Sume			ipxpcb.ipxp_lport++;
112158115Sume			if ((ipxpcb.ipxp_lport < IPXPORT_RESERVED) ||
113158115Sume			    (ipxpcb.ipxp_lport >= IPXPORT_WELLKNOWN))
114158115Sume				ipxpcb.ipxp_lport = IPXPORT_RESERVED;
115158115Sume			lport = htons(ipxpcb.ipxp_lport);
116158115Sume		} while (ipx_pcblookup(&zeroipx_addr, lport, 0));
117158115Sume	ipxp->ipxp_lport = lport;
118158115Sume	return (0);
119158115Sume}
120158115Sume
121158115Sume/*
122158115Sume * Connect from a socket to a specified address.
123158115Sume * Both address and port must be specified in argument sipx.
124158115Sume * If don't have a local address for this socket yet,
125158115Sume * then pick one.
126158115Sume */
127158115Sumeint
128158115Sumeipx_pcbconnect(ipxp, nam, p)
129158115Sume	struct ipxpcb *ipxp;
130158115Sume	struct sockaddr *nam;
131158115Sume	struct proc *p;
132158115Sume{
133158115Sume	struct ipx_ifaddr *ia;
134158115Sume	register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)nam;
135158115Sume	register struct ipx_addr *dst;
136158115Sume	register struct route *ro;
137158115Sume	struct ifnet *ifp;
138158115Sume
139158115Sume	ia = NULL;
140158115Sume
141158115Sume	if (sipx->sipx_family != AF_IPX)
142158115Sume		return (EAFNOSUPPORT);
143158115Sume	if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr))
144158115Sume		return (EADDRNOTAVAIL);
145158115Sume	/*
146158115Sume	 * If we haven't bound which network number to use as ours,
147158115Sume	 * we will use the number of the outgoing interface.
148158115Sume	 * This depends on having done a routing lookup, which
149158115Sume	 * we will probably have to do anyway, so we might
150158115Sume	 * as well do it now.  On the other hand if we are
151158115Sume	 * sending to multiple destinations we may have already
152158115Sume	 * done the lookup, so see if we can use the route
153158115Sume	 * from before.  In any case, we only
154158115Sume	 * chose a port number once, even if sending to multiple
155158115Sume	 * destinations.
156158115Sume	 */
157158115Sume	ro = &ipxp->ipxp_route;
158158115Sume	dst = &satoipx_addr(ro->ro_dst);
159158115Sume	if (ipxp->ipxp_socket->so_options & SO_DONTROUTE)
160158115Sume		goto flush;
161158115Sume	if (!ipx_neteq(ipxp->ipxp_lastdst, sipx->sipx_addr))
162158115Sume		goto flush;
163158115Sume	if (!ipx_hosteq(ipxp->ipxp_lastdst, sipx->sipx_addr)) {
164158115Sume		if (ro->ro_rt != NULL && !(ro->ro_rt->rt_flags & RTF_HOST)) {
165158115Sume			/* can patch route to avoid rtalloc */
166158115Sume			*dst = sipx->sipx_addr;
167158115Sume		} else {
168158115Sume	flush:
169158115Sume			if (ro->ro_rt != NULL)
170158115Sume				RTFREE(ro->ro_rt);
171158115Sume			ro->ro_rt = NULL;
172158115Sume			ipxp->ipxp_laddr.x_net = ipx_zeronet;
173158115Sume		}
174158115Sume	}/* else cached route is ok; do nothing */
175158115Sume	ipxp->ipxp_lastdst = sipx->sipx_addr;
176158115Sume	if ((ipxp->ipxp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
177158115Sume	    (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL)) {
178158115Sume		    /* No route yet, so try to acquire one */
179158115Sume		    ro->ro_dst.sa_family = AF_IPX;
180158115Sume		    ro->ro_dst.sa_len = sizeof(ro->ro_dst);
181158115Sume		    *dst = sipx->sipx_addr;
182158115Sume		    dst->x_port = 0;
183158115Sume		    rtalloc(ro);
184158115Sume	}
185158115Sume	if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) {
186158115Sume		/*
187158115Sume		 * If route is known or can be allocated now,
188158115Sume		 * our src addr is taken from the i/f, else punt.
189158115Sume		 */
190158115Sume
191158115Sume		/*
192158115Sume		 * If we found a route, use the address
193158115Sume		 * corresponding to the outgoing interface
194158115Sume		 */
195158115Sume		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
196158115Sume			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
197158115Sume				if (ia->ia_ifp == ifp)
198158115Sume					break;
199158115Sume		if (ia == NULL) {
200158115Sume			u_short fport = sipx->sipx_addr.x_port;
201158115Sume			sipx->sipx_addr.x_port = 0;
202158115Sume			ia = (struct ipx_ifaddr *)
203158115Sume				ifa_ifwithdstaddr((struct sockaddr *)sipx);
204158115Sume			sipx->sipx_addr.x_port = fport;
205158115Sume			if (ia == NULL)
206158115Sume				ia = ipx_iaonnetof(&sipx->sipx_addr);
207158115Sume			if (ia == NULL)
208158115Sume				ia = ipx_ifaddr;
209158115Sume			if (ia == NULL)
210158115Sume				return (EADDRNOTAVAIL);
211158115Sume		}
212158115Sume		ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net;
213158115Sume	}
214158115Sume	if (ipx_nullhost(ipxp->ipxp_laddr)) {
215158115Sume		/*
216158115Sume		 * If route is known or can be allocated now,
217158115Sume		 * our src addr is taken from the i/f, else punt.
218158115Sume		 */
219158115Sume
220158115Sume		/*
221158115Sume		 * If we found a route, use the address
222158115Sume		 * corresponding to the outgoing interface
223158115Sume		 */
224158115Sume		if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL)
225158115Sume			for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
226158115Sume				if (ia->ia_ifp == ifp)
227158115Sume					break;
228158115Sume		if (ia == NULL) {
229158115Sume			u_short fport = sipx->sipx_addr.x_port;
230158115Sume			sipx->sipx_addr.x_port = 0;
231158115Sume			ia = (struct ipx_ifaddr *)
232158115Sume				ifa_ifwithdstaddr((struct sockaddr *)sipx);
233158115Sume			sipx->sipx_addr.x_port = fport;
234158115Sume			if (ia == NULL)
235158115Sume				ia = ipx_iaonnetof(&sipx->sipx_addr);
236158115Sume			if (ia == NULL)
237158115Sume				ia = ipx_ifaddr;
238158115Sume			if (ia == NULL)
239158115Sume				return (EADDRNOTAVAIL);
240158115Sume		}
241158115Sume		ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host;
242158115Sume	}
243158115Sume	if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0))
244158115Sume		return (EADDRINUSE);
245158115Sume	if (ipxp->ipxp_lport == 0)
246158115Sume		ipx_pcbbind(ipxp, (struct sockaddr *)NULL, p);
247158115Sume
248158115Sume	/* XXX just leave it zero if we can't find a route */
249158115Sume
250158115Sume	ipxp->ipxp_faddr = sipx->sipx_addr;
251158115Sume	/* Includes ipxp->ipxp_fport = sipx->sipx_port; */
252158115Sume	return (0);
253158115Sume}
254158115Sume
255158115Sumevoid
256158115Sumeipx_pcbdisconnect(ipxp)
257158115Sume	struct ipxpcb *ipxp;
258158115Sume{
259158115Sume
260158115Sume	ipxp->ipxp_faddr = zeroipx_addr;
261158115Sume	if (ipxp->ipxp_socket->so_state & SS_NOFDREF)
262158115Sume		ipx_pcbdetach(ipxp);
263158115Sume}
264158115Sume
265158115Sumevoid
266158115Sumeipx_pcbdetach(ipxp)
267158115Sume	struct ipxpcb *ipxp;
268158115Sume{
269158115Sume	struct socket *so = ipxp->ipxp_socket;
270158115Sume
271158115Sume	so->so_pcb = 0;
272158115Sume	sofree(so);
273158115Sume	if (ipxp->ipxp_route.ro_rt != NULL)
274158115Sume		rtfree(ipxp->ipxp_route.ro_rt);
275158115Sume	remque(ipxp);
276158115Sume	FREE(ipxp, M_PCB);
277158115Sume}
278158115Sume
279158115Sumevoid
280145626Sumeipx_setsockaddr(ipxp, nam)
281157779Sume	register struct ipxpcb *ipxp;
2823070Spst	struct sockaddr **nam;
283157779Sume{
284145626Sume	struct sockaddr_ipx *sipx, ssipx;
285157779Sume
286145626Sume	sipx = &ssipx;
287157779Sume	bzero((caddr_t)sipx, sizeof(*sipx));
288157779Sume	sipx->sipx_len = sizeof(*sipx);
289157779Sume	sipx->sipx_family = AF_IPX;
290145626Sume	sipx->sipx_addr = ipxp->ipxp_laddr;
291145626Sume	*nam = dup_sockaddr((struct sockaddr *)sipx, 0);
292145626Sume}
293157779Sume
294145626Sumevoid
295157779Sumeipx_setpeeraddr(ipxp, nam)
296145626Sume	register struct ipxpcb *ipxp;
297145626Sume	struct sockaddr **nam;
298157779Sume{
299157779Sume	struct sockaddr_ipx *sipx, ssipx;
300145626Sume
301157779Sume	sipx = &ssipx;
302157779Sume	bzero((caddr_t)sipx, sizeof(*sipx));
303157779Sume	sipx->sipx_len = sizeof(*sipx);
304145626Sume	sipx->sipx_family = AF_IPX;
305157779Sume	sipx->sipx_addr = ipxp->ipxp_faddr;
306157779Sume	*nam = dup_sockaddr((struct sockaddr *)sipx, 0);
307157779Sume}
308157779Sume
309157779Sume/*
310157779Sume * Pass some notification to all connections of a protocol
311157779Sume * associated with address dst.  Call the
312157779Sume * protocol specific routine to handle each connection.
313157779Sume * Also pass an extra paramter via the ipxpcb. (which may in fact
314157779Sume * be a parameter list!)
315157779Sume */
316157779Sumevoid
317157779Sumeipx_pcbnotify(dst, errno, notify, param)
318157779Sume	register struct ipx_addr *dst;
319157779Sume	int errno;
320157779Sume	void (*notify)(struct ipxpcb *);
321157779Sume	long param;
322157779Sume{
323157779Sume	register struct ipxpcb *ipxp, *oinp;
324157779Sume	int s = splimp();
325157779Sume
326157779Sume	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb);) {
327157779Sume		if (!ipx_hosteq(*dst,ipxp->ipxp_faddr)) {
328157779Sume	next:
329157779Sume			ipxp = ipxp->ipxp_next;
330157779Sume			continue;
331157779Sume		}
332157779Sume		if (ipxp->ipxp_socket == 0)
333157779Sume			goto next;
334157779Sume		if (errno)
335157779Sume			ipxp->ipxp_socket->so_error = errno;
336157779Sume		oinp = ipxp;
337157779Sume		ipxp = ipxp->ipxp_next;
338157779Sume		oinp->ipxp_notify_param = param;
339157779Sume		(*notify)(oinp);
340157779Sume	}
341157779Sume	splx(s);
342145626Sume}
343145626Sume
344145626Sume#ifdef notdef
345157779Sume/*
346157779Sume * After a routing change, flush old routing
347145626Sume * and allocate a (hopefully) better one.
348158115Sume */
349158115Sumeipx_rtchange(ipxp)
350158115Sume	struct ipxpcb *ipxp;
351158115Sume{
352158115Sume	if (ipxp->ipxp_route.ro_rt != NULL) {
353158115Sume		rtfree(ipxp->ipxp_route.ro_rt);
35465532Snectar		ipxp->ipxp_route.ro_rt = NULL;
35565532Snectar		/*
35665532Snectar		 * A new route can be allocated the next time
35765532Snectar		 * output is attempted.
358158115Sume		 */
359158115Sume	}
360158115Sume	/* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
36165532Snectar}
362145626Sume#endif
363211276Sume
36465532Snectarstruct ipxpcb *
365157779Sumeipx_pcblookup(faddr, lport, wildp)
366157779Sume	struct ipx_addr *faddr;
367157779Sume	u_short lport;
368145626Sume	int wildp;
369211276Sume{
370211276Sume	register struct ipxpcb *ipxp, *match = 0;
371211276Sume	int matchwild = 3, wildcard;
372211276Sume	u_short fport;
373211276Sume
3743070Spst	fport = faddr->x_port;
3753070Spst	for (ipxp = (&ipxpcb)->ipxp_next; ipxp != (&ipxpcb); ipxp = ipxp->ipxp_next) {
376145626Sume		if (ipxp->ipxp_lport != lport)
377157779Sume			continue;
378157779Sume		wildcard = 0;
3793070Spst		if (ipx_nullhost(ipxp->ipxp_faddr)) {
380158115Sume			if (!ipx_nullhost(*faddr))
381158115Sume				wildcard++;
382158115Sume		} else {
383158115Sume			if (ipx_nullhost(*faddr))
384158115Sume				wildcard++;
385158115Sume			else {
38665532Snectar				if (!ipx_hosteq(ipxp->ipxp_faddr, *faddr))
38765532Snectar					continue;
38865532Snectar				if (ipxp->ipxp_fport != fport) {
38965532Snectar					if (ipxp->ipxp_fport != 0)
390158115Sume						continue;
391158115Sume					else
392158115Sume						wildcard++;
39365532Snectar				}
394145626Sume			}
395211276Sume		}
3963070Spst		if (wildcard && wildp == 0)
397157779Sume			continue;
398157779Sume		if (wildcard < matchwild) {
399157779Sume			match = ipxp;
40065532Snectar			matchwild = wildcard;
401211276Sume			if (wildcard == 0)
402211276Sume				break;
403211276Sume		}
404211276Sume	}
405211276Sume	return (match);
4063070Spst}
4073070Spst