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