ipx.c revision 194602
1/*-
2 * Copyright (c) 1984, 1985, 1986, 1987, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * Copyright (c) 1995, Mike Mitchell
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 *    notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 *    notice, this list of conditions and the following disclaimer in the
38 *    documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 *    must display the following acknowledgement:
41 *	This product includes software developed by the University of
42 *	California, Berkeley and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 *    may be used to endorse or promote products derived from this software
45 *    without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * SUCH DAMAGE.
58 *
59 *	@(#)ipx.c
60 */
61
62#include <sys/cdefs.h>
63__FBSDID("$FreeBSD: head/sys/netipx/ipx.c 194602 2009-06-21 19:30:33Z rwatson $");
64
65#include <sys/param.h>
66#include <sys/kernel.h>
67#include <sys/systm.h>
68#include <sys/malloc.h>
69#include <sys/priv.h>
70#include <sys/sockio.h>
71#include <sys/socket.h>
72
73#include <net/if.h>
74#include <net/route.h>
75
76#include <netipx/ipx.h>
77#include <netipx/ipx_if.h>
78#include <netipx/ipx_var.h>
79
80/*
81 * XXXRW: Requires synchronization.
82 */
83struct ipx_ifaddr *ipx_ifaddr;
84
85static void	ipx_ifscrub(struct ifnet *ifp, struct ipx_ifaddr *ia);
86static int	ipx_ifinit(struct ifnet *ifp, struct ipx_ifaddr *ia,
87		    struct sockaddr_ipx *sipx, int scrub);
88
89/*
90 * Generic internet control operations (ioctl's).
91 */
92int
93ipx_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
94    struct thread *td)
95{
96	struct ifreq *ifr = (struct ifreq *)data;
97	struct ipx_aliasreq *ifra = (struct ipx_aliasreq *)data;
98	struct ipx_ifaddr *ia;
99	struct ifaddr *ifa;
100	struct ipx_ifaddr *oia;
101	int dstIsNew, hostIsNew;
102	int error = 0, priv;
103
104	/*
105	 * Find address for this interface, if it exists.
106	 */
107	if (ifp == NULL)
108		return (EADDRNOTAVAIL);
109	for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
110		if (ia->ia_ifp == ifp)
111			break;
112
113	switch (cmd) {
114	case SIOCGIFADDR:
115		if (ia == NULL)
116			return (EADDRNOTAVAIL);
117		*(struct sockaddr_ipx *)&ifr->ifr_addr = ia->ia_addr;
118		return (0);
119
120	case SIOCGIFBRDADDR:
121		if (ia == NULL)
122			return (EADDRNOTAVAIL);
123		if ((ifp->if_flags & IFF_BROADCAST) == 0)
124			return (EINVAL);
125		*(struct sockaddr_ipx *)&ifr->ifr_dstaddr = ia->ia_broadaddr;
126		return (0);
127
128	case SIOCGIFDSTADDR:
129		if (ia == NULL)
130			return (EADDRNOTAVAIL);
131		if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
132			return (EINVAL);
133		*(struct sockaddr_ipx *)&ifr->ifr_dstaddr = ia->ia_dstaddr;
134		return (0);
135	}
136
137	switch (cmd) {
138	case SIOCAIFADDR:
139	case SIOCDIFADDR:
140		priv = (cmd == SIOCAIFADDR) ? PRIV_NET_ADDIFADDR :
141		    PRIV_NET_DELIFADDR;
142		if (td && (error = priv_check(td, priv)) != 0)
143			return (error);
144		if (ifra->ifra_addr.sipx_family == AF_IPX)
145		    for (oia = ia; ia != NULL; ia = ia->ia_next) {
146			if (ia->ia_ifp == ifp  &&
147			    ipx_neteq(ia->ia_addr.sipx_addr,
148				  ifra->ifra_addr.sipx_addr))
149			    break;
150		    }
151		if (cmd == SIOCDIFADDR && ia == NULL)
152			return (EADDRNOTAVAIL);
153		/* FALLTHROUGH */
154
155	case SIOCSIFADDR:
156	case SIOCSIFDSTADDR:
157		if (td && (error = priv_check(td, PRIV_NET_SETLLADDR)) != 0)
158			return (error);
159		if (ia == NULL) {
160			oia = (struct ipx_ifaddr *)
161				malloc(sizeof(*ia), M_IFADDR,
162				M_WAITOK | M_ZERO);
163			if (oia == NULL)
164				return (ENOBUFS);
165			if ((ia = ipx_ifaddr) != NULL) {
166				for ( ; ia->ia_next != NULL; ia = ia->ia_next)
167					;
168				ia->ia_next = oia;
169			} else
170				ipx_ifaddr = oia;
171			ia = oia;
172			ifa = (struct ifaddr *)ia;
173			ifa_init(ifa);
174			TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
175			ia->ia_ifp = ifp;
176			ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
177
178			ifa->ifa_netmask = (struct sockaddr *)&ipx_netmask;
179
180			ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
181			if (ifp->if_flags & IFF_BROADCAST) {
182				ia->ia_broadaddr.sipx_family = AF_IPX;
183				ia->ia_broadaddr.sipx_len =
184				    sizeof(ia->ia_addr);
185				ia->ia_broadaddr.sipx_addr.x_host =
186				    ipx_broadhost;
187			}
188		}
189		break;
190
191	default:
192		if (td && (error = priv_check(td, PRIV_NET_HWIOCTL)) != 0)
193			return (error);
194	}
195
196	switch (cmd) {
197	case SIOCSIFDSTADDR:
198		if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
199			return (EINVAL);
200		if (ia->ia_flags & IFA_ROUTE) {
201			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
202			ia->ia_flags &= ~IFA_ROUTE;
203		}
204		if (ifp->if_ioctl) {
205			error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR,
206			    (void *)ia);
207			if (error)
208				return (error);
209		}
210		*(struct sockaddr *)&ia->ia_dstaddr = ifr->ifr_dstaddr;
211		return (0);
212
213	case SIOCSIFADDR:
214		return (ipx_ifinit(ifp, ia,
215				(struct sockaddr_ipx *)&ifr->ifr_addr, 1));
216
217	case SIOCDIFADDR:
218		ipx_ifscrub(ifp, ia);
219		ifa = (struct ifaddr *)ia;
220		TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
221		oia = ia;
222		if (oia == (ia = ipx_ifaddr)) {
223			ipx_ifaddr = ia->ia_next;
224		} else {
225			while (ia->ia_next && (ia->ia_next != oia)) {
226				ia = ia->ia_next;
227			}
228			if (ia->ia_next)
229			    ia->ia_next = oia->ia_next;
230			else
231				printf("Didn't unlink ipxifadr from list\n");
232		}
233		ifa_free(&oia->ia_ifa);
234		return (0);
235
236	case SIOCAIFADDR:
237		dstIsNew = 0;
238		hostIsNew = 1;
239		if (ia->ia_addr.sipx_family == AF_IPX) {
240			if (ifra->ifra_addr.sipx_len == 0) {
241				ifra->ifra_addr = ia->ia_addr;
242				hostIsNew = 0;
243			} else if (ipx_neteq(ifra->ifra_addr.sipx_addr,
244					 ia->ia_addr.sipx_addr))
245				hostIsNew = 0;
246		}
247		if ((ifp->if_flags & IFF_POINTOPOINT) &&
248		    (ifra->ifra_dstaddr.sipx_family == AF_IPX)) {
249			if (hostIsNew == 0)
250				ipx_ifscrub(ifp, ia);
251			ia->ia_dstaddr = ifra->ifra_dstaddr;
252			dstIsNew  = 1;
253		}
254		if (ifra->ifra_addr.sipx_family == AF_IPX &&
255					    (hostIsNew || dstIsNew))
256			error = ipx_ifinit(ifp, ia, &ifra->ifra_addr, 0);
257		return (error);
258
259	default:
260		if (ifp->if_ioctl == NULL)
261			return (EOPNOTSUPP);
262		return ((*ifp->if_ioctl)(ifp, cmd, data));
263	}
264}
265
266/*
267 * Delete any previous route for an old address.
268 */
269static void
270ipx_ifscrub(struct ifnet *ifp, struct ipx_ifaddr *ia)
271{
272
273	if (ia->ia_flags & IFA_ROUTE) {
274		if (ifp->if_flags & IFF_POINTOPOINT) {
275			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
276		} else
277			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
278		ia->ia_flags &= ~IFA_ROUTE;
279	}
280}
281
282/*
283 * Initialize an interface's internet address and routing table entry.
284 */
285static int
286ipx_ifinit(struct ifnet *ifp, struct ipx_ifaddr *ia,
287    struct sockaddr_ipx *sipx, int scrub)
288{
289	struct sockaddr_ipx oldaddr;
290	int s = splimp(), error;
291
292	/*
293	 * Set up new addresses.
294	 */
295	oldaddr = ia->ia_addr;
296	ia->ia_addr = *sipx;
297
298	/*
299	 * The convention we shall adopt for naming is that a supplied
300	 * address of zero means that "we don't care".  Use the MAC address
301	 * of the interface.  If it is an interface without a MAC address,
302	 * like a serial line, the address must be supplied.
303	 *
304	 * Give the interface a chance to initialize if this is its first
305	 * address, and to validate the address if necessary.
306	 */
307	if (ifp->if_ioctl != NULL &&
308	    (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (void *)ia))) {
309		ia->ia_addr = oldaddr;
310		splx(s);
311		return (error);
312	}
313	splx(s);
314	ia->ia_ifa.ifa_metric = ifp->if_metric;
315
316	/*
317	 * Add route for the network.
318	 */
319	if (scrub) {
320		ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
321		ipx_ifscrub(ifp, ia);
322		ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
323	}
324	if (ifp->if_flags & IFF_POINTOPOINT)
325		rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
326	else {
327		ia->ia_broadaddr.sipx_addr.x_net = ia->ia_addr.sipx_addr.x_net;
328		rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP);
329	}
330	ia->ia_flags |= IFA_ROUTE;
331	return (0);
332}
333
334/*
335 * Return address info for specified internet network.
336 */
337struct ipx_ifaddr *
338ipx_iaonnetof(struct ipx_addr *dst)
339{
340	struct ipx_ifaddr *ia;
341	struct ipx_addr *compare;
342	struct ifnet *ifp;
343	struct ipx_ifaddr *ia_maybe = NULL;
344	union ipx_net net = dst->x_net;
345
346	for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) {
347		if ((ifp = ia->ia_ifp) != NULL) {
348			if (ifp->if_flags & IFF_POINTOPOINT) {
349				compare = &satoipx_addr(ia->ia_dstaddr);
350				if (ipx_hosteq(*dst, *compare))
351					return (ia);
352				if (ipx_neteqnn(net,
353				    ia->ia_addr.sipx_addr.x_net))
354					ia_maybe = ia;
355			} else {
356				if (ipx_neteqnn(net,
357				    ia->ia_addr.sipx_addr.x_net))
358					return (ia);
359			}
360		}
361	}
362	return (ia_maybe);
363}
364
365void
366ipx_printhost(struct ipx_addr *addr)
367{
368	u_short port;
369	struct ipx_addr work = *addr;
370	char *p; u_char *q;
371	char *net = "", *host = "";
372	char cport[10], chost[15], cnet[15];
373
374	port = ntohs(work.x_port);
375
376	if (ipx_nullnet(work) && ipx_nullhost(work)) {
377		if (port)
378			printf("*.%x", port);
379		else
380			printf("*.*");
381
382		return;
383	}
384
385	if (ipx_wildnet(work))
386		net = "any";
387	else if (ipx_nullnet(work))
388		net = "*";
389	else {
390		q = work.x_net.c_net;
391		snprintf(cnet, sizeof(cnet), "%x%x%x%x",
392			q[0], q[1], q[2], q[3]);
393		for (p = cnet; *p == '0' && p < cnet + 8; p++)
394			continue;
395		net = p;
396	}
397
398	if (ipx_wildhost(work))
399		host = "any";
400	else if (ipx_nullhost(work))
401		host = "*";
402	else {
403		q = work.x_host.c_host;
404		snprintf(chost, sizeof(chost), "%x%x%x%x%x%x",
405			q[0], q[1], q[2], q[3], q[4], q[5]);
406		for (p = chost; *p == '0' && p < chost + 12; p++)
407			continue;
408		host = p;
409	}
410
411	if (port) {
412		if (strcmp(host, "*") == 0) {
413			host = "";
414			snprintf(cport, sizeof(cport), "%x", port);
415		} else
416			snprintf(cport, sizeof(cport), ".%x", port);
417	} else
418		*cport = 0;
419
420	printf("%s.%s%s", net, host, cport);
421}
422