ipx.c revision 22975
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.c
35 *
36 * $Id$
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/ioctl.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
55#ifdef IPX
56
57struct ipx_ifaddr *ipx_ifaddr;
58int ipx_interfaces;
59
60/*
61 * Generic internet control operations (ioctl's).
62 */
63/* ARGSUSED */
64int
65ipx_control(so, cmd, data, ifp)
66	struct socket *so;
67	int cmd;
68	caddr_t data;
69	register struct ifnet *ifp;
70{
71	register struct ifreq *ifr = (struct ifreq *)data;
72	register struct ipx_aliasreq *ifra = (struct ipx_aliasreq *)data;
73	register struct ipx_ifaddr *ia;
74	struct ifaddr *ifa;
75	struct ipx_ifaddr *oia;
76	int dstIsNew, hostIsNew;
77	int error = 0;
78
79	/*
80	 * Find address for this interface, if it exists.
81	 */
82	if (ifp == 0)
83		return (EADDRNOTAVAIL);
84	for (ia = ipx_ifaddr; ia; ia = ia->ia_next)
85		if (ia->ia_ifp == ifp)
86			break;
87
88	switch (cmd) {
89
90	case SIOCGIFADDR:
91		if (ia == (struct ipx_ifaddr *)0)
92			return (EADDRNOTAVAIL);
93		*(struct sockaddr_ipx *)&ifr->ifr_addr = ia->ia_addr;
94		return (0);
95
96	case SIOCGIFBRDADDR:
97		if (ia == (struct ipx_ifaddr *)0)
98			return (EADDRNOTAVAIL);
99		if ((ifp->if_flags & IFF_BROADCAST) == 0)
100			return (EINVAL);
101		*(struct sockaddr_ipx *)&ifr->ifr_dstaddr = ia->ia_broadaddr;
102		return (0);
103
104	case SIOCGIFDSTADDR:
105		if (ia == (struct ipx_ifaddr *)0)
106			return (EADDRNOTAVAIL);
107		if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
108			return (EINVAL);
109		*(struct sockaddr_ipx *)&ifr->ifr_dstaddr = ia->ia_dstaddr;
110		return (0);
111	}
112
113	if ((so->so_state & SS_PRIV) == 0)
114		return (EPERM);
115
116	switch (cmd) {
117	case SIOCAIFADDR:
118	case SIOCDIFADDR:
119		if (ifra->ifra_addr.sipx_family == AF_IPX)
120		    for (oia = ia; ia; ia = ia->ia_next) {
121			if (ia->ia_ifp == ifp  &&
122			    ipx_neteq(ia->ia_addr.sipx_addr,
123				  ifra->ifra_addr.sipx_addr))
124			    break;
125		    }
126		if (cmd == SIOCDIFADDR && ia == 0)
127			return (EADDRNOTAVAIL);
128		/* FALLTHROUGH */
129
130	case SIOCSIFADDR:
131	case SIOCSIFDSTADDR:
132		if (ia == (struct ipx_ifaddr *)0) {
133			oia = (struct ipx_ifaddr *)
134				malloc(sizeof *ia, M_IFADDR, M_WAITOK);
135			if (oia == (struct ipx_ifaddr *)NULL)
136				return (ENOBUFS);
137			bzero((caddr_t)oia, sizeof(*oia));
138			if ((ia = ipx_ifaddr)) {
139				for ( ; ia->ia_next; ia = ia->ia_next)
140					;
141				ia->ia_next = oia;
142			} else
143				ipx_ifaddr = oia;
144			ia = oia;
145			ifa = (struct ifaddr *)ia;
146			TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
147			ia->ia_ifp = ifp;
148			ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
149
150			ifa->ifa_netmask = (struct sockaddr *)&ipx_netmask;
151
152			ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
153			if (ifp->if_flags & IFF_BROADCAST) {
154				ia->ia_broadaddr.sipx_family = AF_IPX;
155				ia->ia_broadaddr.sipx_len = sizeof(ia->ia_addr);
156				ia->ia_broadaddr.sipx_addr.x_host = ipx_broadhost;
157			}
158			ipx_interfaces++;
159		}
160	}
161
162	switch (cmd) {
163
164	case SIOCSIFDSTADDR:
165		if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
166			return (EINVAL);
167		if (ia->ia_flags & IFA_ROUTE) {
168			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
169			ia->ia_flags &= ~IFA_ROUTE;
170		}
171		if (ifp->if_ioctl) {
172			error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, (void *)ia);
173			if (error)
174				return (error);
175		}
176		*(struct sockaddr *)&ia->ia_dstaddr = ifr->ifr_dstaddr;
177		return (0);
178
179	case SIOCSIFADDR:
180		return (ipx_ifinit(ifp, ia,
181				(struct sockaddr_ipx *)&ifr->ifr_addr, 1));
182
183	case SIOCDIFADDR:
184		ipx_ifscrub(ifp, ia);
185		ifa = (struct ifaddr *)ia;
186		TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
187		oia = ia;
188		if (oia == (ia = ipx_ifaddr)) {
189			ipx_ifaddr = ia->ia_next;
190		} else {
191			while (ia->ia_next && (ia->ia_next != oia)) {
192				ia = ia->ia_next;
193			}
194			if (ia->ia_next)
195			    ia->ia_next = oia->ia_next;
196			else
197				printf("Didn't unlink ipxifadr from list\n");
198		}
199		IFAFREE((&oia->ia_ifa));
200		if (0 == --ipx_interfaces) {
201			/*
202			 * We reset to virginity and start all over again
203			 */
204			ipx_thishost = ipx_zerohost;
205		}
206		return (0);
207
208	case SIOCAIFADDR:
209		dstIsNew = 0; hostIsNew = 1;
210		if (ia->ia_addr.sipx_family == AF_IPX) {
211			if (ifra->ifra_addr.sipx_len == 0) {
212				ifra->ifra_addr = ia->ia_addr;
213				hostIsNew = 0;
214			} else if (ipx_neteq(ifra->ifra_addr.sipx_addr,
215					 ia->ia_addr.sipx_addr))
216				hostIsNew = 0;
217		}
218		if ((ifp->if_flags & IFF_POINTOPOINT) &&
219		    (ifra->ifra_dstaddr.sipx_family == AF_IPX)) {
220			if (hostIsNew == 0)
221				ipx_ifscrub(ifp, ia);
222			ia->ia_dstaddr = ifra->ifra_dstaddr;
223			dstIsNew  = 1;
224		}
225		if (ifra->ifra_addr.sipx_family == AF_IPX &&
226					    (hostIsNew || dstIsNew))
227			error = ipx_ifinit(ifp, ia, &ifra->ifra_addr, 0);
228		return (error);
229
230	default:
231		if (ifp->if_ioctl == 0)
232			return (EOPNOTSUPP);
233		return ((*ifp->if_ioctl)(ifp, cmd, data));
234	}
235}
236
237/*
238* Delete any previous route for an old address.
239*/
240void
241ipx_ifscrub(ifp, ia)
242	register struct ifnet *ifp;
243	register struct ipx_ifaddr *ia;
244{
245	if (ia->ia_flags & IFA_ROUTE) {
246		if (ifp->if_flags & IFF_POINTOPOINT) {
247			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
248		} else
249			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
250		ia->ia_flags &= ~IFA_ROUTE;
251	}
252}
253/*
254 * Initialize an interface's internet address
255 * and routing table entry.
256 */
257int
258ipx_ifinit(ifp, ia, sipx, scrub)
259	register struct ifnet *ifp;
260	register struct ipx_ifaddr *ia;
261	register struct sockaddr_ipx *sipx;
262	int scrub;
263{
264	struct sockaddr_ipx oldaddr;
265	register union ipx_host *h = &ia->ia_addr.sipx_addr.x_host;
266	int s = splimp(), error;
267
268	/*
269	 * Set up new addresses.
270	 */
271	oldaddr = ia->ia_addr;
272	ia->ia_addr = *sipx;
273	/*
274	 * The convention we shall adopt for naming is that
275	 * a supplied address of zero means that "we don't care".
276	 * if there is a single interface, use the address of that
277	 * interface as our 6 byte host address.
278	 * if there are multiple interfaces, use any address already
279	 * used.
280	 *
281	 * Give the interface a chance to initialize
282	 * if this is its first address,
283	 * and to validate the address if necessary.
284	 */
285	if (ipx_hosteqnh(ipx_thishost, ipx_zerohost)) {
286		if (ifp->if_ioctl &&
287		     (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (void *)ia))) {
288			ia->ia_addr = oldaddr;
289			splx(s);
290			return (error);
291		}
292		ipx_thishost = *h;
293	} else if (ipx_hosteqnh(sipx->sipx_addr.x_host, ipx_zerohost)
294	    || ipx_hosteqnh(sipx->sipx_addr.x_host, ipx_thishost)) {
295		*h = ipx_thishost;
296		if (ifp->if_ioctl &&
297		     (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (void *)ia))) {
298			ia->ia_addr = oldaddr;
299			splx(s);
300			return (error);
301		}
302		if (!ipx_hosteqnh(ipx_thishost,*h)) {
303			ia->ia_addr = oldaddr;
304			splx(s);
305			return (EINVAL);
306		}
307	} else {
308		ia->ia_addr = oldaddr;
309		splx(s);
310		return (EINVAL);
311	}
312	ia->ia_ifa.ifa_metric = ifp->if_metric;
313	/*
314	 * Add route for the network.
315	 */
316	if (scrub) {
317		ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
318		ipx_ifscrub(ifp, ia);
319		ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
320	}
321	if (ifp->if_flags & IFF_POINTOPOINT)
322		rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
323	else {
324		ia->ia_broadaddr.sipx_addr.x_net = ia->ia_addr.sipx_addr.x_net;
325		rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP);
326	}
327	ia->ia_flags |= IFA_ROUTE;
328	return (0);
329}
330
331/*
332 * Return address info for specified internet network.
333 */
334struct ipx_ifaddr *
335ipx_iaonnetof(dst)
336	register struct ipx_addr *dst;
337{
338	register struct ipx_ifaddr *ia;
339	register struct ipx_addr *compare;
340	register struct ifnet *ifp;
341	struct ipx_ifaddr *ia_maybe = 0;
342	union ipx_net net = dst->x_net;
343
344	for (ia = ipx_ifaddr; ia; ia = ia->ia_next) {
345		if ((ifp = ia->ia_ifp)) {
346			if (ifp->if_flags & IFF_POINTOPOINT) {
347				compare = &satoipx_addr(ia->ia_dstaddr);
348				if (ipx_hosteq(*dst, *compare))
349					return (ia);
350				if (ipx_neteqnn(net, ia->ia_addr.sipx_addr.x_net))
351					ia_maybe = ia;
352			} else {
353				if (ipx_neteqnn(net, ia->ia_addr.sipx_addr.x_net))
354					return (ia);
355			}
356		}
357	}
358	return (ia_maybe);
359}
360#endif
361