ipx.c revision 25652
138889Sjdp/*
238889Sjdp * Copyright (c) 1995, Mike Mitchell
3130561Sobrien * Copyright (c) 1984, 1985, 1986, 1987, 1993
4218822Sdim *	The Regents of the University of California.  All rights reserved.
538889Sjdp *
660484Sobrien * Redistribution and use in source and binary forms, with or without
7130561Sobrien * modification, are permitted provided that the following conditions
8130561Sobrien * are met:
9130561Sobrien * 1. Redistributions of source code must retain the above copyright
1038889Sjdp *    notice, this list of conditions and the following disclaimer.
1138889Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1238889Sjdp *    notice, this list of conditions and the following disclaimer in the
1360484Sobrien *    documentation and/or other materials provided with the distribution.
1460484Sobrien * 3. All advertising materials mentioning features or use of this software
1538889Sjdp *    must display the following acknowledgement:
16218822Sdim *	This product includes software developed by the University of
17218822Sdim *	California, Berkeley and its contributors.
18218822Sdim * 4. Neither the name of the University nor the names of its contributors
19218822Sdim *    may be used to endorse or promote products derived from this software
2077298Sobrien *    without specific prior written permission.
21218822Sdim *
2277298Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2377298Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2460484Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2538889Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2638889Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2738889Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2838889Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2938889Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3038889Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3138889Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3238889Sjdp * SUCH DAMAGE.
3338889Sjdp *
3438889Sjdp *	@(#)ipx.c
3538889Sjdp *
3638889Sjdp * $Id: ipx.c,v 1.9 1997/05/01 06:21:27 jhay Exp $
3738889Sjdp */
38218822Sdim
3938889Sjdp#include <sys/param.h>
4038889Sjdp#include <sys/queue.h>
4138889Sjdp#include <sys/systm.h>
4238889Sjdp#include <sys/mbuf.h>
4338889Sjdp#include <sys/sockio.h>
4438889Sjdp#include <sys/proc.h>
4538889Sjdp#include <sys/protosw.h>
4638889Sjdp#include <sys/errno.h>
4738889Sjdp#include <sys/socket.h>
4838889Sjdp#include <sys/socketvar.h>
4960484Sobrien
50218822Sdim#include <net/if.h>
51218822Sdim#include <net/route.h>
5277298Sobrien
53218822Sdim#include <netipx/ipx.h>
5438889Sjdp#include <netipx/ipx_if.h>
5560484Sobrien#include <netipx/ipx_var.h>
56104834Sobrien
5760484Sobrienstruct ipx_ifaddr *ipx_ifaddr;
58104834Sobrien
5938889Sjdpstatic	void ipx_ifscrub(struct ifnet *ifp, struct ipx_ifaddr *ia);
6038889Sjdpstatic	int ipx_ifinit(struct ifnet *ifp, struct ipx_ifaddr *ia,
6160484Sobrien		       struct sockaddr_ipx *sipx, int scrub);
6238889Sjdp
6338889Sjdp/*
6438889Sjdp * Generic internet control operations (ioctl's).
65218822Sdim */
66130561Sobrienint
67218822Sdimipx_control(so, cmd, data, ifp, p)
6838889Sjdp	struct socket *so;
6977298Sobrien	int cmd;
7038889Sjdp	caddr_t data;
71218822Sdim	register struct ifnet *ifp;
7260484Sobrien	struct proc *p;
73218822Sdim{
7438889Sjdp	register struct ifreq *ifr = (struct ifreq *)data;
7589857Sobrien	register struct ipx_aliasreq *ifra = (struct ipx_aliasreq *)data;
7638889Sjdp	register struct ipx_ifaddr *ia;
7738889Sjdp	struct ifaddr *ifa;
78130561Sobrien	struct ipx_ifaddr *oia;
79218822Sdim	int dstIsNew, hostIsNew;
8038889Sjdp	int error = 0;
8189857Sobrien
8291041Sobrien	/*
8389857Sobrien	 * Find address for this interface, if it exists.
8460484Sobrien	 */
8538889Sjdp	if (ifp == NULL)
8689857Sobrien		return (EADDRNOTAVAIL);
87218822Sdim	for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next)
8838889Sjdp		if (ia->ia_ifp == ifp)
89104834Sobrien			break;
9038889Sjdp
91218822Sdim	switch (cmd) {
9238889Sjdp
93130561Sobrien	case SIOCGIFADDR:
9477298Sobrien		if (ia == NULL)
95218822Sdim			return (EADDRNOTAVAIL);
9638889Sjdp		*(struct sockaddr_ipx *)&ifr->ifr_addr = ia->ia_addr;
97218822Sdim		return (0);
9889857Sobrien
99130561Sobrien	case SIOCGIFBRDADDR:
100218822Sdim		if (ia == NULL)
10138889Sjdp			return (EADDRNOTAVAIL);
10238889Sjdp		if ((ifp->if_flags & IFF_BROADCAST) == 0)
10338889Sjdp			return (EINVAL);
10478828Sobrien		*(struct sockaddr_ipx *)&ifr->ifr_dstaddr = ia->ia_broadaddr;
10538889Sjdp		return (0);
10638889Sjdp
10738889Sjdp	case SIOCGIFDSTADDR:
10838889Sjdp		if (ia == NULL)
10938889Sjdp			return (EADDRNOTAVAIL);
11038889Sjdp		if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
111218822Sdim			return (EINVAL);
11238889Sjdp		*(struct sockaddr_ipx *)&ifr->ifr_dstaddr = ia->ia_dstaddr;
11338889Sjdp		return (0);
11438889Sjdp	}
11538889Sjdp
11638889Sjdp	if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
11738889Sjdp		return (error);
11838889Sjdp
11938889Sjdp	switch (cmd) {
12038889Sjdp	case SIOCAIFADDR:
12138889Sjdp	case SIOCDIFADDR:
12238889Sjdp		if (ifra->ifra_addr.sipx_family == AF_IPX)
123218822Sdim		    for (oia = ia; ia != NULL; ia = ia->ia_next) {
12438889Sjdp			if (ia->ia_ifp == ifp  &&
12538889Sjdp			    ipx_neteq(ia->ia_addr.sipx_addr,
126218822Sdim				  ifra->ifra_addr.sipx_addr))
12738889Sjdp			    break;
128218822Sdim		    }
129218822Sdim		if (cmd == SIOCDIFADDR && ia == NULL)
130218822Sdim			return (EADDRNOTAVAIL);
13138889Sjdp		/* FALLTHROUGH */
13238889Sjdp
13338889Sjdp	case SIOCSIFADDR:
13438889Sjdp	case SIOCSIFDSTADDR:
13538889Sjdp		if (ia == NULL) {
136218822Sdim			oia = (struct ipx_ifaddr *)
137218822Sdim				malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
138218822Sdim			if (oia == NULL)
139218822Sdim				return (ENOBUFS);
14038889Sjdp			bzero((caddr_t)oia, sizeof(*oia));
14138889Sjdp			if ((ia = ipx_ifaddr) != NULL) {
14238889Sjdp				for ( ; ia->ia_next != NULL; ia = ia->ia_next)
14338889Sjdp					;
14438889Sjdp				ia->ia_next = oia;
14538889Sjdp			} else
14638889Sjdp				ipx_ifaddr = oia;
14738889Sjdp			ia = oia;
14838889Sjdp			ifa = (struct ifaddr *)ia;
14938889Sjdp			TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
15060484Sobrien			ia->ia_ifp = ifp;
15138889Sjdp			ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
15277298Sobrien
15360484Sobrien			ifa->ifa_netmask = (struct sockaddr *)&ipx_netmask;
15460484Sobrien
15538889Sjdp			ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
15660484Sobrien			if (ifp->if_flags & IFF_BROADCAST) {
15760484Sobrien				ia->ia_broadaddr.sipx_family = AF_IPX;
15860484Sobrien				ia->ia_broadaddr.sipx_len = sizeof(ia->ia_addr);
15977298Sobrien				ia->ia_broadaddr.sipx_addr.x_host = ipx_broadhost;
16060484Sobrien			}
16177298Sobrien		}
16277298Sobrien	}
16377298Sobrien
16477298Sobrien	switch (cmd) {
16560484Sobrien
16660484Sobrien	case SIOCSIFDSTADDR:
16760484Sobrien		if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
16860484Sobrien			return (EINVAL);
16960484Sobrien		if (ia->ia_flags & IFA_ROUTE) {
17038889Sjdp			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
17138889Sjdp			ia->ia_flags &= ~IFA_ROUTE;
17238889Sjdp		}
17338889Sjdp		if (ifp->if_ioctl) {
17438889Sjdp			error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, (void *)ia);
17538889Sjdp			if (error)
17638889Sjdp				return (error);
17738889Sjdp		}
17838889Sjdp		*(struct sockaddr *)&ia->ia_dstaddr = ifr->ifr_dstaddr;
17938889Sjdp		return (0);
18060484Sobrien
181130561Sobrien	case SIOCSIFADDR:
18238889Sjdp		return (ipx_ifinit(ifp, ia,
18338889Sjdp				(struct sockaddr_ipx *)&ifr->ifr_addr, 1));
18438889Sjdp
18538889Sjdp	case SIOCDIFADDR:
18638889Sjdp		ipx_ifscrub(ifp, ia);
18738889Sjdp		ifa = (struct ifaddr *)ia;
18838889Sjdp		TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
18938889Sjdp		oia = ia;
19038889Sjdp		if (oia == (ia = ipx_ifaddr)) {
19138889Sjdp			ipx_ifaddr = ia->ia_next;
19238889Sjdp		} else {
19338889Sjdp			while (ia->ia_next && (ia->ia_next != oia)) {
19438889Sjdp				ia = ia->ia_next;
19538889Sjdp			}
19638889Sjdp			if (ia->ia_next)
19738889Sjdp			    ia->ia_next = oia->ia_next;
19838889Sjdp			else
19938889Sjdp				printf("Didn't unlink ipxifadr from list\n");
20038889Sjdp		}
20138889Sjdp		IFAFREE((&oia->ia_ifa));
20238889Sjdp		return (0);
20338889Sjdp
204130561Sobrien	case SIOCAIFADDR:
20538889Sjdp		dstIsNew = 0;
20638889Sjdp		hostIsNew = 1;
20738889Sjdp		if (ia->ia_addr.sipx_family == AF_IPX) {
20860484Sobrien			if (ifra->ifra_addr.sipx_len == 0) {
20938889Sjdp				ifra->ifra_addr = ia->ia_addr;
21038889Sjdp				hostIsNew = 0;
21160484Sobrien			} else if (ipx_neteq(ifra->ifra_addr.sipx_addr,
21260484Sobrien					 ia->ia_addr.sipx_addr))
213130561Sobrien				hostIsNew = 0;
21438889Sjdp		}
21538889Sjdp		if ((ifp->if_flags & IFF_POINTOPOINT) &&
21638889Sjdp		    (ifra->ifra_dstaddr.sipx_family == AF_IPX)) {
21738889Sjdp			if (hostIsNew == 0)
21838889Sjdp				ipx_ifscrub(ifp, ia);
21938889Sjdp			ia->ia_dstaddr = ifra->ifra_dstaddr;
22038889Sjdp			dstIsNew  = 1;
22138889Sjdp		}
222218822Sdim		if (ifra->ifra_addr.sipx_family == AF_IPX &&
22338889Sjdp					    (hostIsNew || dstIsNew))
22438889Sjdp			error = ipx_ifinit(ifp, ia, &ifra->ifra_addr, 0);
22538889Sjdp		return (error);
22638889Sjdp
22738889Sjdp	default:
22838889Sjdp		if (ifp->if_ioctl == NULL)
22938889Sjdp			return (EOPNOTSUPP);
23038889Sjdp		return ((*ifp->if_ioctl)(ifp, cmd, data));
23138889Sjdp	}
23238889Sjdp}
23338889Sjdp
23438889Sjdp/*
23538889Sjdp* Delete any previous route for an old address.
23638889Sjdp*/
23738889Sjdpstatic void
23838889Sjdpipx_ifscrub(ifp, ia)
23938889Sjdp	register struct ifnet *ifp;
24038889Sjdp	register struct ipx_ifaddr *ia;
24138889Sjdp{
24260484Sobrien	if (ia->ia_flags & IFA_ROUTE) {
243218822Sdim		if (ifp->if_flags & IFF_POINTOPOINT) {
244218822Sdim			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
24577298Sobrien		} else
246218822Sdim			rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
24738889Sjdp		ia->ia_flags &= ~IFA_ROUTE;
24860484Sobrien	}
249104834Sobrien}
25060484Sobrien/*
251104834Sobrien * Initialize an interface's internet address
25238889Sjdp * and routing table entry.
25338889Sjdp */
25477298Sobrienstatic int
25560484Sobrienipx_ifinit(ifp, ia, sipx, scrub)
25638889Sjdp	register struct ifnet *ifp;
25738889Sjdp	register struct ipx_ifaddr *ia;
25838889Sjdp	register struct sockaddr_ipx *sipx;
259130561Sobrien	int scrub;
260218822Sdim{
26138889Sjdp	struct sockaddr_ipx oldaddr;
26277298Sobrien	int s = splimp(), error;
26338889Sjdp
26460484Sobrien	/*
265218822Sdim	 * Set up new addresses.
26638889Sjdp	 */
26789857Sobrien	oldaddr = ia->ia_addr;
26838889Sjdp	ia->ia_addr = *sipx;
26938889Sjdp
270130561Sobrien	/*
27138889Sjdp	 * The convention we shall adopt for naming is that
27289857Sobrien	 * a supplied address of zero means that "we don't care".
27391041Sobrien	 * Use the MAC address of the interface. If it is an
27489857Sobrien	 * interface without a MAC address, like a serial line, the
27560484Sobrien	 * address must be supplied.
27638889Sjdp	 *
27789857Sobrien	 * Give the interface a chance to initialize
278218822Sdim	 * if this is its first address,
27938889Sjdp	 * and to validate the address if necessary.
28091041Sobrien	 */
28138889Sjdp	if (ifp->if_ioctl != NULL &&
282218822Sdim	    (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (void *)ia))) {
28338889Sjdp		ia->ia_addr = oldaddr;
28477298Sobrien		splx(s);
28538889Sjdp		return (error);
28638889Sjdp	}
28789857Sobrien	splx(s);
288218822Sdim	ia->ia_ifa.ifa_metric = ifp->if_metric;
289130561Sobrien	/*
290218822Sdim	 * Add route for the network.
29138889Sjdp	 */
29238889Sjdp	if (scrub) {
29338889Sjdp		ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
29438889Sjdp		ipx_ifscrub(ifp, ia);
29538889Sjdp		ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
29638889Sjdp	}
29760484Sobrien	if (ifp->if_flags & IFF_POINTOPOINT)
298218822Sdim		rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
299218822Sdim	else {
30077298Sobrien		ia->ia_broadaddr.sipx_addr.x_net = ia->ia_addr.sipx_addr.x_net;
301218822Sdim		rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP);
30238889Sjdp	}
30360484Sobrien	ia->ia_flags |= IFA_ROUTE;
304104834Sobrien	return (0);
30560484Sobrien}
306104834Sobrien
30738889Sjdp/*
30838889Sjdp * Return address info for specified internet network.
30977298Sobrien */
31060484Sobrienstruct ipx_ifaddr *
31138889Sjdpipx_iaonnetof(dst)
31238889Sjdp	register struct ipx_addr *dst;
31338889Sjdp{
314130561Sobrien	register struct ipx_ifaddr *ia;
315218822Sdim	register struct ipx_addr *compare;
31638889Sjdp	register struct ifnet *ifp;
31777298Sobrien	struct ipx_ifaddr *ia_maybe = NULL;
31838889Sjdp	union ipx_net net = dst->x_net;
31960484Sobrien
320218822Sdim	for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) {
32138889Sjdp		if ((ifp = ia->ia_ifp) != NULL) {
32289857Sobrien			if (ifp->if_flags & IFF_POINTOPOINT) {
32338889Sjdp				compare = &satoipx_addr(ia->ia_dstaddr);
32438889Sjdp				if (ipx_hosteq(*dst, *compare))
325130561Sobrien					return (ia);
32638889Sjdp				if (ipx_neteqnn(net, ia->ia_addr.sipx_addr.x_net))
32789857Sobrien					ia_maybe = ia;
32891041Sobrien			} else {
32989857Sobrien				if (ipx_neteqnn(net, ia->ia_addr.sipx_addr.x_net))
33060484Sobrien					return (ia);
33138889Sjdp			}
33289857Sobrien		}
333218822Sdim	}
33438889Sjdp	return (ia_maybe);
33591041Sobrien}
33638889Sjdp