xref: /freebsd/sys/net/if.c (revision 0b59d917d8e7d01802bf2e0693634b8d1e94d529)
1df8bae1dSRodney W. Grimes /*
2df8bae1dSRodney W. Grimes  * Copyright (c) 1980, 1986, 1993
3df8bae1dSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
4df8bae1dSRodney W. Grimes  *
5df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
6df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
7df8bae1dSRodney W. Grimes  * are met:
8df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
9df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
10df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
11df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
12df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
13df8bae1dSRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
14df8bae1dSRodney W. Grimes  *    must display the following acknowledgement:
15df8bae1dSRodney W. Grimes  *	This product includes software developed by the University of
16df8bae1dSRodney W. Grimes  *	California, Berkeley and its contributors.
17df8bae1dSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
18df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
19df8bae1dSRodney W. Grimes  *    without specific prior written permission.
20df8bae1dSRodney W. Grimes  *
21df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
32df8bae1dSRodney W. Grimes  *
33df8bae1dSRodney W. Grimes  *	@(#)if.c	8.3 (Berkeley) 1/4/94
34c3aac50fSPeter Wemm  * $FreeBSD$
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
375591b823SEivind Eklund #include "opt_compat.h"
38cfa1ca9dSYoshinobu Inoue #include "opt_inet6.h"
390d0f9d1eSYoshinobu Inoue #include "opt_inet.h"
405591b823SEivind Eklund 
41df8bae1dSRodney W. Grimes #include <sys/param.h>
424d1d4912SBruce Evans #include <sys/malloc.h>
43df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
44df8bae1dSRodney W. Grimes #include <sys/systm.h>
45df8bae1dSRodney W. Grimes #include <sys/proc.h>
46df8bae1dSRodney W. Grimes #include <sys/socket.h>
47df8bae1dSRodney W. Grimes #include <sys/socketvar.h>
48df8bae1dSRodney W. Grimes #include <sys/protosw.h>
49df8bae1dSRodney W. Grimes #include <sys/kernel.h>
5051a53488SBruce Evans #include <sys/sockio.h>
51963e4c2aSGarrett Wollman #include <sys/syslog.h>
52602d513cSGarrett Wollman #include <sys/sysctl.h>
5391421ba2SRobert Watson #include <sys/jail.h>
54df8bae1dSRodney W. Grimes 
55df8bae1dSRodney W. Grimes #include <net/if.h>
56b106252cSBill Paul #include <net/if_arp.h>
57df8bae1dSRodney W. Grimes #include <net/if_dl.h>
5866ce51ceSArchie Cobbs #include <net/if_types.h>
5930aad87dSBrooks Davis #include <net/if_var.h>
609448326fSPoul-Henning Kamp #include <net/radix.h>
615500d3beSWarner Losh #include <net/route.h>
62df8bae1dSRodney W. Grimes 
630d0f9d1eSYoshinobu Inoue #if defined(INET) || defined(INET6)
6482cd038dSYoshinobu Inoue /*XXX*/
6582cd038dSYoshinobu Inoue #include <netinet/in.h>
660d0f9d1eSYoshinobu Inoue #include <netinet/in_var.h>
673411310dSYoshinobu Inoue #ifdef INET6
68978ee2edSJun-ichiro itojun Hagino #include <netinet6/in6_var.h>
69978ee2edSJun-ichiro itojun Hagino #include <netinet6/in6_ifattach.h>
703411310dSYoshinobu Inoue #endif
7182cd038dSYoshinobu Inoue #endif
7282cd038dSYoshinobu Inoue 
730b59d917SJonathan Lemon static int	ifconf(u_long, caddr_t);
740b59d917SJonathan Lemon static void	ifinit(void *);
750b59d917SJonathan Lemon static void	if_qflush(struct ifqueue *);
760b59d917SJonathan Lemon static void	if_slowtimo(void *);
770b59d917SJonathan Lemon static void	link_rtrequest(int, struct rtentry *, struct sockaddr *);
780b59d917SJonathan Lemon static int	if_rtdel(struct radix_node *, void *);
790b59d917SJonathan Lemon static struct	if_clone *if_clone_lookup(const char *, int *);
800b59d917SJonathan Lemon static int	if_clone_list(struct if_clonereq *);
8182cd038dSYoshinobu Inoue #ifdef INET6
8282cd038dSYoshinobu Inoue /*
8382cd038dSYoshinobu Inoue  * XXX: declare here to avoid to include many inet6 related files..
8482cd038dSYoshinobu Inoue  * should be more generalized?
8582cd038dSYoshinobu Inoue  */
8682cd038dSYoshinobu Inoue extern void	nd6_setmtu __P((struct ifnet *));
8782cd038dSYoshinobu Inoue #endif
8882cd038dSYoshinobu Inoue 
890b59d917SJonathan Lemon int	if_index = 0;
900b59d917SJonathan Lemon struct	ifaddr **ifnet_addrs;
910b59d917SJonathan Lemon struct	ifnet **ifindex2ifnet = NULL;
920b59d917SJonathan Lemon int	ifqmaxlen = IFQ_MAXLEN;
930b59d917SJonathan Lemon struct	ifnethead ifnet;	/* depend on static init XXX */
9430aad87dSBrooks Davis int	if_cloners_count;
950b59d917SJonathan Lemon LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
960b59d917SJonathan Lemon 
970b59d917SJonathan Lemon /*
980b59d917SJonathan Lemon  * System initialization
990b59d917SJonathan Lemon  */
1000b59d917SJonathan Lemon SYSINIT(interfaces, SI_SUB_PROTO_IF, SI_ORDER_FIRST, ifinit, NULL)
1010b59d917SJonathan Lemon 
1020b59d917SJonathan Lemon MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address");
1030b59d917SJonathan Lemon MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address");
10430aad87dSBrooks Davis 
105df8bae1dSRodney W. Grimes /*
106df8bae1dSRodney W. Grimes  * Network interface utility routines.
107df8bae1dSRodney W. Grimes  *
108df8bae1dSRodney W. Grimes  * Routines with ifa_ifwith* names take sockaddr *'s as
109df8bae1dSRodney W. Grimes  * parameters.
110df8bae1dSRodney W. Grimes  */
1112b14f991SJulian Elischer /* ARGSUSED*/
112df8bae1dSRodney W. Grimes void
11327501cb6SBruce Evans ifinit(dummy)
11427501cb6SBruce Evans 	void *dummy;
115df8bae1dSRodney W. Grimes {
1168ba5bdaeSPeter Wemm 	struct ifnet *ifp;
1178ba5bdaeSPeter Wemm 	int s;
118df8bae1dSRodney W. Grimes 
1198ba5bdaeSPeter Wemm 	s = splimp();
120fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
121e0ea20bcSPoul-Henning Kamp 		if (ifp->if_snd.ifq_maxlen == 0) {
122e0ea20bcSPoul-Henning Kamp 			printf("%s%d XXX: driver didn't set ifq_maxlen\n",
123e0ea20bcSPoul-Henning Kamp 			    ifp->if_name, ifp->if_unit);
124df8bae1dSRodney W. Grimes 			ifp->if_snd.ifq_maxlen = ifqmaxlen;
125e0ea20bcSPoul-Henning Kamp 		}
1265e980e22SJohn Baldwin 		if (!mtx_initialized(&ifp->if_snd.ifq_mtx)) {
127df5e1987SJonathan Lemon 			printf("%s%d XXX: driver didn't initialize queue mtx\n",
128df5e1987SJonathan Lemon 			    ifp->if_name, ifp->if_unit);
129df5e1987SJonathan Lemon 			mtx_init(&ifp->if_snd.ifq_mtx, "unknown", MTX_DEF);
130df5e1987SJonathan Lemon 		}
131df5e1987SJonathan Lemon 	}
1328ba5bdaeSPeter Wemm 	splx(s);
133df8bae1dSRodney W. Grimes 	if_slowtimo(0);
134df8bae1dSRodney W. Grimes }
135df8bae1dSRodney W. Grimes 
136df8bae1dSRodney W. Grimes /*
137df8bae1dSRodney W. Grimes  * Attach an interface to the
138df8bae1dSRodney W. Grimes  * list of "active" interfaces.
139df8bae1dSRodney W. Grimes  */
140df8bae1dSRodney W. Grimes void
141df8bae1dSRodney W. Grimes if_attach(ifp)
142df8bae1dSRodney W. Grimes 	struct ifnet *ifp;
143df8bae1dSRodney W. Grimes {
144df8bae1dSRodney W. Grimes 	unsigned socksize, ifasize;
1451ce9bf88SPoul-Henning Kamp 	int namelen, masklen;
1461ce9bf88SPoul-Henning Kamp 	char workbuf[64];
147df8bae1dSRodney W. Grimes 	register struct sockaddr_dl *sdl;
148df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
149df8bae1dSRodney W. Grimes 	static int if_indexlim = 8;
15029412182SGarrett Wollman 	static int inited;
151f23b4c91SGarrett Wollman 
15229412182SGarrett Wollman 	if (!inited) {
15329412182SGarrett Wollman 		TAILQ_INIT(&ifnet);
15429412182SGarrett Wollman 		inited = 1;
15529412182SGarrett Wollman 	}
156df8bae1dSRodney W. Grimes 
15729412182SGarrett Wollman 	TAILQ_INSERT_TAIL(&ifnet, ifp, if_link);
158df8bae1dSRodney W. Grimes 	ifp->if_index = ++if_index;
15959562606SGarrett Wollman 	/*
16059562606SGarrett Wollman 	 * XXX -
16159562606SGarrett Wollman 	 * The old code would work if the interface passed a pre-existing
16259562606SGarrett Wollman 	 * chain of ifaddrs to this code.  We don't trust our callers to
16359562606SGarrett Wollman 	 * properly initialize the tailq, however, so we no longer allow
16459562606SGarrett Wollman 	 * this unlikely case.
16559562606SGarrett Wollman 	 */
16659562606SGarrett Wollman 	TAILQ_INIT(&ifp->if_addrhead);
16782cd038dSYoshinobu Inoue 	TAILQ_INIT(&ifp->if_prefixhead);
1686817526dSPoul-Henning Kamp 	TAILQ_INIT(&ifp->if_multiaddrs);
16998b9590eSPoul-Henning Kamp 	getmicrotime(&ifp->if_lastchange);
170df8bae1dSRodney W. Grimes 	if (ifnet_addrs == 0 || if_index >= if_indexlim) {
171df8bae1dSRodney W. Grimes 		unsigned n = (if_indexlim <<= 1) * sizeof(ifa);
1727cc0979fSDavid Malone 		caddr_t q = malloc(n, M_IFADDR, M_WAITOK | M_ZERO);
173df8bae1dSRodney W. Grimes 		if (ifnet_addrs) {
174df8bae1dSRodney W. Grimes 			bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2);
175df8bae1dSRodney W. Grimes 			free((caddr_t)ifnet_addrs, M_IFADDR);
176df8bae1dSRodney W. Grimes 		}
17782cd038dSYoshinobu Inoue 		ifnet_addrs = (struct ifaddr **)q;
17882cd038dSYoshinobu Inoue 
17982cd038dSYoshinobu Inoue 		/* grow ifindex2ifnet */
18082cd038dSYoshinobu Inoue 		n = if_indexlim * sizeof(struct ifnet *);
1817cc0979fSDavid Malone 		q = malloc(n, M_IFADDR, M_WAITOK | M_ZERO);
18282cd038dSYoshinobu Inoue 		if (ifindex2ifnet) {
18382cd038dSYoshinobu Inoue 			bcopy((caddr_t)ifindex2ifnet, q, n/2);
18482cd038dSYoshinobu Inoue 			free((caddr_t)ifindex2ifnet, M_IFADDR);
185df8bae1dSRodney W. Grimes 		}
18682cd038dSYoshinobu Inoue 		ifindex2ifnet = (struct ifnet **)q;
18782cd038dSYoshinobu Inoue 	}
18882cd038dSYoshinobu Inoue 
18982cd038dSYoshinobu Inoue 	ifindex2ifnet[if_index] = ifp;
19082cd038dSYoshinobu Inoue 
191df5e1987SJonathan Lemon 	mtx_init(&ifp->if_snd.ifq_mtx, ifp->if_name, MTX_DEF);
192df5e1987SJonathan Lemon 
193df8bae1dSRodney W. Grimes 	/*
194df8bae1dSRodney W. Grimes 	 * create a Link Level name for this device
195df8bae1dSRodney W. Grimes 	 */
1962127f260SArchie Cobbs 	namelen = snprintf(workbuf, sizeof(workbuf),
1972127f260SArchie Cobbs 	    "%s%d", ifp->if_name, ifp->if_unit);
198df8bae1dSRodney W. Grimes #define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
1991ce9bf88SPoul-Henning Kamp 	masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
200df8bae1dSRodney W. Grimes 	socksize = masklen + ifp->if_addrlen;
201df8bae1dSRodney W. Grimes #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
202df8bae1dSRodney W. Grimes 	if (socksize < sizeof(*sdl))
203df8bae1dSRodney W. Grimes 		socksize = sizeof(*sdl);
2048a261b8fSDoug Rabson 	socksize = ROUNDUP(socksize);
205df8bae1dSRodney W. Grimes 	ifasize = sizeof(*ifa) + 2 * socksize;
2067cc0979fSDavid Malone 	ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK | M_ZERO);
2079448326fSPoul-Henning Kamp 	if (ifa) {
208df8bae1dSRodney W. Grimes 		sdl = (struct sockaddr_dl *)(ifa + 1);
209df8bae1dSRodney W. Grimes 		sdl->sdl_len = socksize;
210df8bae1dSRodney W. Grimes 		sdl->sdl_family = AF_LINK;
2111ce9bf88SPoul-Henning Kamp 		bcopy(workbuf, sdl->sdl_data, namelen);
2121ce9bf88SPoul-Henning Kamp 		sdl->sdl_nlen = namelen;
213df8bae1dSRodney W. Grimes 		sdl->sdl_index = ifp->if_index;
214df8bae1dSRodney W. Grimes 		sdl->sdl_type = ifp->if_type;
215df8bae1dSRodney W. Grimes 		ifnet_addrs[if_index - 1] = ifa;
216df8bae1dSRodney W. Grimes 		ifa->ifa_ifp = ifp;
217df8bae1dSRodney W. Grimes 		ifa->ifa_rtrequest = link_rtrequest;
218df8bae1dSRodney W. Grimes 		ifa->ifa_addr = (struct sockaddr *)sdl;
219df8bae1dSRodney W. Grimes 		sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
220df8bae1dSRodney W. Grimes 		ifa->ifa_netmask = (struct sockaddr *)sdl;
221df8bae1dSRodney W. Grimes 		sdl->sdl_len = masklen;
222df8bae1dSRodney W. Grimes 		while (namelen != 0)
223df8bae1dSRodney W. Grimes 			sdl->sdl_data[--namelen] = 0xff;
22459562606SGarrett Wollman 		TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link);
225df8bae1dSRodney W. Grimes 	}
226df8bae1dSRodney W. Grimes }
2276182fdbdSPeter Wemm 
2286182fdbdSPeter Wemm /*
2296182fdbdSPeter Wemm  * Detach an interface, removing it from the
2306182fdbdSPeter Wemm  * list of "active" interfaces.
2316182fdbdSPeter Wemm  */
2326182fdbdSPeter Wemm void
2336182fdbdSPeter Wemm if_detach(ifp)
2346182fdbdSPeter Wemm 	struct ifnet *ifp;
2356182fdbdSPeter Wemm {
2366182fdbdSPeter Wemm 	struct ifaddr *ifa;
2375500d3beSWarner Losh 	struct radix_node_head	*rnh;
2385500d3beSWarner Losh 	int s;
2395500d3beSWarner Losh 	int i;
2406182fdbdSPeter Wemm 
2416182fdbdSPeter Wemm 	/*
2426182fdbdSPeter Wemm 	 * Remove routes and flush queues.
2436182fdbdSPeter Wemm 	 */
2445500d3beSWarner Losh 	s = splnet();
2456182fdbdSPeter Wemm 	if_down(ifp);
2466182fdbdSPeter Wemm 
2476182fdbdSPeter Wemm 	/*
2486182fdbdSPeter Wemm 	 * Remove address from ifnet_addrs[] and maybe decrement if_index.
2496182fdbdSPeter Wemm 	 * Clean up all addresses.
2506182fdbdSPeter Wemm 	 */
251aa6be122SWarner Losh 	ifnet_addrs[ifp->if_index - 1] = 0;
252aa6be122SWarner Losh 	while (if_index > 0 && ifnet_addrs[if_index - 1] == 0)
2536182fdbdSPeter Wemm 		if_index--;
2546182fdbdSPeter Wemm 
2556182fdbdSPeter Wemm 	for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa;
2566182fdbdSPeter Wemm 	     ifa = TAILQ_FIRST(&ifp->if_addrhead)) {
2570d0f9d1eSYoshinobu Inoue #ifdef INET
258aa6be122SWarner Losh 		/* XXX: Ugly!! ad hoc just for INET */
259aa6be122SWarner Losh 		if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
260aa6be122SWarner Losh 			struct ifaliasreq ifr;
261aa6be122SWarner Losh 
262aa6be122SWarner Losh 			bzero(&ifr, sizeof(ifr));
263aa6be122SWarner Losh 			ifr.ifra_addr = *ifa->ifa_addr;
264aa6be122SWarner Losh 			if (ifa->ifa_dstaddr)
265aa6be122SWarner Losh 				ifr.ifra_broadaddr = *ifa->ifa_dstaddr;
266aa6be122SWarner Losh 			if (in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp,
267aa6be122SWarner Losh 			    NULL) == 0)
268aa6be122SWarner Losh 				continue;
269aa6be122SWarner Losh 		}
2700d0f9d1eSYoshinobu Inoue #endif /* INET */
2710d0f9d1eSYoshinobu Inoue #ifdef INET6
2720d0f9d1eSYoshinobu Inoue 		if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
27333841545SHajimu UMEMOTO 			in6_purgeaddr(ifa);
274978ee2edSJun-ichiro itojun Hagino 			/* ifp_addrhead is already updated */
2750d0f9d1eSYoshinobu Inoue 			continue;
2760d0f9d1eSYoshinobu Inoue 		}
2770d0f9d1eSYoshinobu Inoue #endif /* INET6 */
2786182fdbdSPeter Wemm 		TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
2796182fdbdSPeter Wemm 		IFAFREE(ifa);
2806182fdbdSPeter Wemm 	}
2816182fdbdSPeter Wemm 
28233841545SHajimu UMEMOTO #ifdef INET6
28333841545SHajimu UMEMOTO 	/*
28433841545SHajimu UMEMOTO 	 * Remove all IPv6 kernel structs related to ifp.  This should be done
28533841545SHajimu UMEMOTO 	 * before removing routing entries below, since IPv6 interface direct
28633841545SHajimu UMEMOTO 	 * routes are expected to be removed by the IPv6-specific kernel API.
28733841545SHajimu UMEMOTO 	 * Otherwise, the kernel will detect some inconsistency and bark it.
28833841545SHajimu UMEMOTO 	 */
28933841545SHajimu UMEMOTO 	in6_ifdetach(ifp);
29033841545SHajimu UMEMOTO #endif
29133841545SHajimu UMEMOTO 
2925500d3beSWarner Losh 	/*
2935500d3beSWarner Losh 	 * Delete all remaining routes using this interface
2945500d3beSWarner Losh 	 * Unfortuneatly the only way to do this is to slog through
2955500d3beSWarner Losh 	 * the entire routing table looking for routes which point
2965500d3beSWarner Losh 	 * to this interface...oh well...
2975500d3beSWarner Losh 	 */
2985500d3beSWarner Losh 	for (i = 1; i <= AF_MAX; i++) {
2995500d3beSWarner Losh 		if ((rnh = rt_tables[i]) == NULL)
3005500d3beSWarner Losh 			continue;
3015500d3beSWarner Losh 		(void) rnh->rnh_walktree(rnh, if_rtdel, ifp);
3025500d3beSWarner Losh 	}
3035500d3beSWarner Losh 
3046182fdbdSPeter Wemm 	TAILQ_REMOVE(&ifnet, ifp, if_link);
305df5e1987SJonathan Lemon 	mtx_destroy(&ifp->if_snd.ifq_mtx);
3065500d3beSWarner Losh 	splx(s);
3075500d3beSWarner Losh }
3085500d3beSWarner Losh 
3095500d3beSWarner Losh /*
3105500d3beSWarner Losh  * Delete Routes for a Network Interface
3115500d3beSWarner Losh  *
3125500d3beSWarner Losh  * Called for each routing entry via the rnh->rnh_walktree() call above
3135500d3beSWarner Losh  * to delete all route entries referencing a detaching network interface.
3145500d3beSWarner Losh  *
3155500d3beSWarner Losh  * Arguments:
3165500d3beSWarner Losh  *	rn	pointer to node in the routing table
3175500d3beSWarner Losh  *	arg	argument passed to rnh->rnh_walktree() - detaching interface
3185500d3beSWarner Losh  *
3195500d3beSWarner Losh  * Returns:
3205500d3beSWarner Losh  *	0	successful
3215500d3beSWarner Losh  *	errno	failed - reason indicated
3225500d3beSWarner Losh  *
3235500d3beSWarner Losh  */
3245500d3beSWarner Losh static int
3255500d3beSWarner Losh if_rtdel(rn, arg)
3265500d3beSWarner Losh 	struct radix_node	*rn;
3275500d3beSWarner Losh 	void			*arg;
3285500d3beSWarner Losh {
3295500d3beSWarner Losh 	struct rtentry	*rt = (struct rtentry *)rn;
3305500d3beSWarner Losh 	struct ifnet	*ifp = arg;
3315500d3beSWarner Losh 	int		err;
3325500d3beSWarner Losh 
3335500d3beSWarner Losh 	if (rt->rt_ifp == ifp) {
3345500d3beSWarner Losh 
3355500d3beSWarner Losh 		/*
3365500d3beSWarner Losh 		 * Protect (sorta) against walktree recursion problems
3375500d3beSWarner Losh 		 * with cloned routes
3385500d3beSWarner Losh 		 */
3395500d3beSWarner Losh 		if ((rt->rt_flags & RTF_UP) == 0)
3405500d3beSWarner Losh 			return (0);
3415500d3beSWarner Losh 
3425500d3beSWarner Losh 		err = rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
3435500d3beSWarner Losh 				rt_mask(rt), rt->rt_flags,
3445500d3beSWarner Losh 				(struct rtentry **) NULL);
3455500d3beSWarner Losh 		if (err) {
3465500d3beSWarner Losh 			log(LOG_WARNING, "if_rtdel: error %d\n", err);
3475500d3beSWarner Losh 		}
3485500d3beSWarner Losh 	}
3495500d3beSWarner Losh 
3505500d3beSWarner Losh 	return (0);
3516182fdbdSPeter Wemm }
3526182fdbdSPeter Wemm 
353df8bae1dSRodney W. Grimes /*
35430aad87dSBrooks Davis  * Create a clone network interface.
35530aad87dSBrooks Davis  */
35630aad87dSBrooks Davis int
35730aad87dSBrooks Davis if_clone_create(name, len)
35830aad87dSBrooks Davis 	char *name;
35930aad87dSBrooks Davis 	int len;
36030aad87dSBrooks Davis {
36130aad87dSBrooks Davis 	struct if_clone *ifc;
36230aad87dSBrooks Davis 	char *dp;
36330aad87dSBrooks Davis 	int wildcard;
36430aad87dSBrooks Davis 	int unit;
36530aad87dSBrooks Davis 	int err;
36630aad87dSBrooks Davis 
36730aad87dSBrooks Davis 	ifc = if_clone_lookup(name, &unit);
36830aad87dSBrooks Davis 	if (ifc == NULL)
36930aad87dSBrooks Davis 		return (EINVAL);
37030aad87dSBrooks Davis 
37130aad87dSBrooks Davis 	if (ifunit(name) != NULL)
37230aad87dSBrooks Davis 		return (EEXIST);
37330aad87dSBrooks Davis 
37430aad87dSBrooks Davis 	wildcard = (unit < 0);
37530aad87dSBrooks Davis 
37630aad87dSBrooks Davis 	err = (*ifc->ifc_create)(ifc, &unit);
37730aad87dSBrooks Davis 	if (err != 0)
37830aad87dSBrooks Davis 		return (err);
37930aad87dSBrooks Davis 
38030aad87dSBrooks Davis 	/* In the wildcard case, we need to update the name. */
38130aad87dSBrooks Davis 	if (wildcard) {
38230aad87dSBrooks Davis 		for (dp = name; *dp != '\0'; dp++);
38330aad87dSBrooks Davis 		if (snprintf(dp, len - (dp-name), "%d", unit) >
38430aad87dSBrooks Davis 		    len - (dp-name) - 1) {
38530aad87dSBrooks Davis 			/*
38630aad87dSBrooks Davis 			 * This can only be a programmer error and
38730aad87dSBrooks Davis 			 * there's no straightforward way to recover if
38830aad87dSBrooks Davis 			 * it happens.
38930aad87dSBrooks Davis 			 */
39030aad87dSBrooks Davis 			panic("if_clone_create(): interface name too long");
39130aad87dSBrooks Davis 		}
39230aad87dSBrooks Davis 
39330aad87dSBrooks Davis 	}
39430aad87dSBrooks Davis 
39530aad87dSBrooks Davis 	return (0);
39630aad87dSBrooks Davis }
39730aad87dSBrooks Davis 
39830aad87dSBrooks Davis /*
39930aad87dSBrooks Davis  * Destroy a clone network interface.
40030aad87dSBrooks Davis  */
40130aad87dSBrooks Davis int
40230aad87dSBrooks Davis if_clone_destroy(name)
40330aad87dSBrooks Davis 	const char *name;
40430aad87dSBrooks Davis {
40530aad87dSBrooks Davis 	struct if_clone *ifc;
40630aad87dSBrooks Davis 	struct ifnet *ifp;
40730aad87dSBrooks Davis 
40830aad87dSBrooks Davis 	ifc = if_clone_lookup(name, NULL);
40930aad87dSBrooks Davis 	if (ifc == NULL)
41030aad87dSBrooks Davis 		return (EINVAL);
41130aad87dSBrooks Davis 
41230aad87dSBrooks Davis 	ifp = ifunit(name);
41330aad87dSBrooks Davis 	if (ifp == NULL)
41430aad87dSBrooks Davis 		return (ENXIO);
41530aad87dSBrooks Davis 
41630aad87dSBrooks Davis 	if (ifc->ifc_destroy == NULL)
41730aad87dSBrooks Davis 		return (EOPNOTSUPP);
41830aad87dSBrooks Davis 
41930aad87dSBrooks Davis 	(*ifc->ifc_destroy)(ifp);
42030aad87dSBrooks Davis 	return (0);
42130aad87dSBrooks Davis }
42230aad87dSBrooks Davis 
42330aad87dSBrooks Davis /*
42430aad87dSBrooks Davis  * Look up a network interface cloner.
42530aad87dSBrooks Davis  */
4260b59d917SJonathan Lemon static struct if_clone *
42730aad87dSBrooks Davis if_clone_lookup(name, unitp)
42830aad87dSBrooks Davis 	const char *name;
42930aad87dSBrooks Davis 	int *unitp;
43030aad87dSBrooks Davis {
43130aad87dSBrooks Davis 	struct if_clone *ifc;
43230aad87dSBrooks Davis 	const char *cp;
43330aad87dSBrooks Davis 	int i;
43430aad87dSBrooks Davis 
43530aad87dSBrooks Davis 	for (ifc = LIST_FIRST(&if_cloners); ifc != NULL;) {
43630aad87dSBrooks Davis 		for (cp = name, i = 0; i < ifc->ifc_namelen; i++, cp++) {
43730aad87dSBrooks Davis 			if (ifc->ifc_name[i] != *cp)
43830aad87dSBrooks Davis 				goto next_ifc;
43930aad87dSBrooks Davis 		}
44030aad87dSBrooks Davis 		goto found_name;
44130aad87dSBrooks Davis  next_ifc:
44230aad87dSBrooks Davis 		ifc = LIST_NEXT(ifc, ifc_list);
44330aad87dSBrooks Davis 	}
44430aad87dSBrooks Davis 
44530aad87dSBrooks Davis 	/* No match. */
44630aad87dSBrooks Davis 	return ((struct if_clone *)NULL);
44730aad87dSBrooks Davis 
44830aad87dSBrooks Davis  found_name:
44930aad87dSBrooks Davis 	if (*cp == '\0') {
45030aad87dSBrooks Davis 		i = -1;
45130aad87dSBrooks Davis 	} else {
45230aad87dSBrooks Davis 		for (i = 0; *cp != '\0'; cp++) {
45330aad87dSBrooks Davis 			if (*cp < '0' || *cp > '9') {
45430aad87dSBrooks Davis 				/* Bogus unit number. */
45530aad87dSBrooks Davis 				return (NULL);
45630aad87dSBrooks Davis 			}
45730aad87dSBrooks Davis 			i = (i * 10) + (*cp - '0');
45830aad87dSBrooks Davis 		}
45930aad87dSBrooks Davis 	}
46030aad87dSBrooks Davis 
46130aad87dSBrooks Davis 	if (unitp != NULL)
46230aad87dSBrooks Davis 		*unitp = i;
46330aad87dSBrooks Davis 	return (ifc);
46430aad87dSBrooks Davis }
46530aad87dSBrooks Davis 
46630aad87dSBrooks Davis /*
46730aad87dSBrooks Davis  * Register a network interface cloner.
46830aad87dSBrooks Davis  */
46930aad87dSBrooks Davis void
47030aad87dSBrooks Davis if_clone_attach(ifc)
47130aad87dSBrooks Davis 	struct if_clone *ifc;
47230aad87dSBrooks Davis {
47330aad87dSBrooks Davis 
47430aad87dSBrooks Davis 	LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
47530aad87dSBrooks Davis 	if_cloners_count++;
47630aad87dSBrooks Davis }
47730aad87dSBrooks Davis 
47830aad87dSBrooks Davis /*
47930aad87dSBrooks Davis  * Unregister a network interface cloner.
48030aad87dSBrooks Davis  */
48130aad87dSBrooks Davis void
48230aad87dSBrooks Davis if_clone_detach(ifc)
48330aad87dSBrooks Davis 	struct if_clone *ifc;
48430aad87dSBrooks Davis {
48530aad87dSBrooks Davis 
48630aad87dSBrooks Davis 	LIST_REMOVE(ifc, ifc_list);
48730aad87dSBrooks Davis 	if_cloners_count--;
48830aad87dSBrooks Davis }
48930aad87dSBrooks Davis 
49030aad87dSBrooks Davis /*
49130aad87dSBrooks Davis  * Provide list of interface cloners to userspace.
49230aad87dSBrooks Davis  */
4930b59d917SJonathan Lemon static int
49430aad87dSBrooks Davis if_clone_list(ifcr)
49530aad87dSBrooks Davis 	struct if_clonereq *ifcr;
49630aad87dSBrooks Davis {
49730aad87dSBrooks Davis 	char outbuf[IFNAMSIZ], *dst;
49830aad87dSBrooks Davis 	struct if_clone *ifc;
49930aad87dSBrooks Davis 	int count, error = 0;
50030aad87dSBrooks Davis 
50130aad87dSBrooks Davis 	ifcr->ifcr_total = if_cloners_count;
50230aad87dSBrooks Davis 	if ((dst = ifcr->ifcr_buffer) == NULL) {
50330aad87dSBrooks Davis 		/* Just asking how many there are. */
50430aad87dSBrooks Davis 		return (0);
50530aad87dSBrooks Davis 	}
50630aad87dSBrooks Davis 
50730aad87dSBrooks Davis 	if (ifcr->ifcr_count < 0)
50830aad87dSBrooks Davis 		return (EINVAL);
50930aad87dSBrooks Davis 
51030aad87dSBrooks Davis 	count = (if_cloners_count < ifcr->ifcr_count) ?
51130aad87dSBrooks Davis 	    if_cloners_count : ifcr->ifcr_count;
51230aad87dSBrooks Davis 
51330aad87dSBrooks Davis 	for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
51430aad87dSBrooks Davis 	     ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
51530aad87dSBrooks Davis 		strncpy(outbuf, ifc->ifc_name, IFNAMSIZ);
51630aad87dSBrooks Davis 		outbuf[IFNAMSIZ - 1] = '\0';	/* sanity */
51730aad87dSBrooks Davis 		error = copyout(outbuf, dst, IFNAMSIZ);
51830aad87dSBrooks Davis 		if (error)
51930aad87dSBrooks Davis 			break;
52030aad87dSBrooks Davis 	}
52130aad87dSBrooks Davis 
52230aad87dSBrooks Davis 	return (error);
52330aad87dSBrooks Davis }
52430aad87dSBrooks Davis 
52530aad87dSBrooks Davis /*
526df8bae1dSRodney W. Grimes  * Locate an interface based on a complete address.
527df8bae1dSRodney W. Grimes  */
528df8bae1dSRodney W. Grimes /*ARGSUSED*/
529df8bae1dSRodney W. Grimes struct ifaddr *
530df8bae1dSRodney W. Grimes ifa_ifwithaddr(addr)
5310b59d917SJonathan Lemon 	struct sockaddr *addr;
532df8bae1dSRodney W. Grimes {
5330b59d917SJonathan Lemon 	struct ifnet *ifp;
5340b59d917SJonathan Lemon 	struct ifaddr *ifa;
535df8bae1dSRodney W. Grimes 
536df8bae1dSRodney W. Grimes #define	equal(a1, a2) \
537df8bae1dSRodney W. Grimes   (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
538fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link)
53937d40066SPoul-Henning Kamp 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
540df8bae1dSRodney W. Grimes 			if (ifa->ifa_addr->sa_family != addr->sa_family)
541df8bae1dSRodney W. Grimes 				continue;
542df8bae1dSRodney W. Grimes 			if (equal(addr, ifa->ifa_addr))
5430b59d917SJonathan Lemon 				goto done;
54482cd038dSYoshinobu Inoue 			/* IP6 doesn't have broadcast */
5450b59d917SJonathan Lemon 			if ((ifp->if_flags & IFF_BROADCAST) &&
5460b59d917SJonathan Lemon 			    ifa->ifa_broadaddr &&
54782cd038dSYoshinobu Inoue 			    ifa->ifa_broadaddr->sa_len != 0 &&
548df8bae1dSRodney W. Grimes 			    equal(ifa->ifa_broadaddr, addr))
5490b59d917SJonathan Lemon 				goto done;
5500b59d917SJonathan Lemon 		}
5510b59d917SJonathan Lemon 	ifa = NULL;
5520b59d917SJonathan Lemon done:
553df8bae1dSRodney W. Grimes 	return (ifa);
554df8bae1dSRodney W. Grimes }
5550b59d917SJonathan Lemon 
556df8bae1dSRodney W. Grimes /*
557df8bae1dSRodney W. Grimes  * Locate the point to point interface with a given destination address.
558df8bae1dSRodney W. Grimes  */
559df8bae1dSRodney W. Grimes /*ARGSUSED*/
560df8bae1dSRodney W. Grimes struct ifaddr *
561df8bae1dSRodney W. Grimes ifa_ifwithdstaddr(addr)
5620b59d917SJonathan Lemon 	struct sockaddr *addr;
563df8bae1dSRodney W. Grimes {
5640b59d917SJonathan Lemon 	struct ifnet *ifp;
5650b59d917SJonathan Lemon 	struct ifaddr *ifa;
566df8bae1dSRodney W. Grimes 
5670b59d917SJonathan Lemon 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
5680b59d917SJonathan Lemon 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
5690b59d917SJonathan Lemon 			continue;
57037d40066SPoul-Henning Kamp 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
571df8bae1dSRodney W. Grimes 			if (ifa->ifa_addr->sa_family != addr->sa_family)
572df8bae1dSRodney W. Grimes 				continue;
57355088a1cSDavid Greenman 			if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))
5740b59d917SJonathan Lemon 				goto done;
575df8bae1dSRodney W. Grimes 		}
5760b59d917SJonathan Lemon 	}
5770b59d917SJonathan Lemon 	ifa = NULL;
5780b59d917SJonathan Lemon done:
5790b59d917SJonathan Lemon 	return (ifa);
580df8bae1dSRodney W. Grimes }
581df8bae1dSRodney W. Grimes 
582df8bae1dSRodney W. Grimes /*
583df8bae1dSRodney W. Grimes  * Find an interface on a specific network.  If many, choice
584df8bae1dSRodney W. Grimes  * is most specific found.
585df8bae1dSRodney W. Grimes  */
586df8bae1dSRodney W. Grimes struct ifaddr *
587df8bae1dSRodney W. Grimes ifa_ifwithnet(addr)
588df8bae1dSRodney W. Grimes 	struct sockaddr *addr;
589df8bae1dSRodney W. Grimes {
590df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
591df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
592df8bae1dSRodney W. Grimes 	struct ifaddr *ifa_maybe = (struct ifaddr *) 0;
593df8bae1dSRodney W. Grimes 	u_int af = addr->sa_family;
594df8bae1dSRodney W. Grimes 	char *addr_data = addr->sa_data, *cplim;
595df8bae1dSRodney W. Grimes 
5967e2a6151SJulian Elischer 	/*
5977e2a6151SJulian Elischer 	 * AF_LINK addresses can be looked up directly by their index number,
5987e2a6151SJulian Elischer 	 * so do that if we can.
5997e2a6151SJulian Elischer 	 */
600df8bae1dSRodney W. Grimes 	if (af == AF_LINK) {
601df8bae1dSRodney W. Grimes 	    register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
602df8bae1dSRodney W. Grimes 	    if (sdl->sdl_index && sdl->sdl_index <= if_index)
603df8bae1dSRodney W. Grimes 		return (ifnet_addrs[sdl->sdl_index - 1]);
604df8bae1dSRodney W. Grimes 	}
6057e2a6151SJulian Elischer 
6067e2a6151SJulian Elischer 	/*
6077e2a6151SJulian Elischer 	 * Scan though each interface, looking for ones that have
6087e2a6151SJulian Elischer 	 * addresses in this address family.
6097e2a6151SJulian Elischer 	 */
610fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
61137d40066SPoul-Henning Kamp 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
612df8bae1dSRodney W. Grimes 			register char *cp, *cp2, *cp3;
613df8bae1dSRodney W. Grimes 
614523a02aaSDavid Greenman 			if (ifa->ifa_addr->sa_family != af)
615df8bae1dSRodney W. Grimes next:				continue;
61682cd038dSYoshinobu Inoue 			if (
61782cd038dSYoshinobu Inoue #ifdef INET6 /* XXX: for maching gif tunnel dst as routing entry gateway */
61882cd038dSYoshinobu Inoue 			    addr->sa_family != AF_INET6 &&
61982cd038dSYoshinobu Inoue #endif
62082cd038dSYoshinobu Inoue 			    ifp->if_flags & IFF_POINTOPOINT) {
6217e2a6151SJulian Elischer 				/*
6227e2a6151SJulian Elischer 				 * This is a bit broken as it doesn't
6237e2a6151SJulian Elischer 				 * take into account that the remote end may
6247e2a6151SJulian Elischer 				 * be a single node in the network we are
6257e2a6151SJulian Elischer 				 * looking for.
6267e2a6151SJulian Elischer 				 * The trouble is that we don't know the
6277e2a6151SJulian Elischer 				 * netmask for the remote end.
6287e2a6151SJulian Elischer 				 */
629fcd6781aSGarrett Wollman 				if (ifa->ifa_dstaddr != 0
630fcd6781aSGarrett Wollman 				    && equal(addr, ifa->ifa_dstaddr))
6310b59d917SJonathan Lemon 					goto done;
6323740e2adSDavid Greenman 			} else {
6337e2a6151SJulian Elischer 				/*
6347ed8f465SJulian Elischer 				 * if we have a special address handler,
6357ed8f465SJulian Elischer 				 * then use it instead of the generic one.
6367ed8f465SJulian Elischer 				 */
6377ed8f465SJulian Elischer 	          		if (ifa->ifa_claim_addr) {
6380b59d917SJonathan Lemon 					if ((*ifa->ifa_claim_addr)(ifa, addr))
6390b59d917SJonathan Lemon 						goto done;
6407ed8f465SJulian Elischer 					continue;
6417ed8f465SJulian Elischer 				}
6427ed8f465SJulian Elischer 
6437ed8f465SJulian Elischer 				/*
6447e2a6151SJulian Elischer 				 * Scan all the bits in the ifa's address.
6457e2a6151SJulian Elischer 				 * If a bit dissagrees with what we are
6467e2a6151SJulian Elischer 				 * looking for, mask it with the netmask
6477e2a6151SJulian Elischer 				 * to see if it really matters.
6487e2a6151SJulian Elischer 				 * (A byte at a time)
6497e2a6151SJulian Elischer 				 */
650523a02aaSDavid Greenman 				if (ifa->ifa_netmask == 0)
651523a02aaSDavid Greenman 					continue;
652df8bae1dSRodney W. Grimes 				cp = addr_data;
653df8bae1dSRodney W. Grimes 				cp2 = ifa->ifa_addr->sa_data;
654df8bae1dSRodney W. Grimes 				cp3 = ifa->ifa_netmask->sa_data;
6557e2a6151SJulian Elischer 				cplim = ifa->ifa_netmask->sa_len
6567e2a6151SJulian Elischer 					+ (char *)ifa->ifa_netmask;
657df8bae1dSRodney W. Grimes 				while (cp3 < cplim)
658df8bae1dSRodney W. Grimes 					if ((*cp++ ^ *cp2++) & *cp3++)
6597e2a6151SJulian Elischer 						goto next; /* next address! */
6607e2a6151SJulian Elischer 				/*
6617e2a6151SJulian Elischer 				 * If the netmask of what we just found
6627e2a6151SJulian Elischer 				 * is more specific than what we had before
6637e2a6151SJulian Elischer 				 * (if we had one) then remember the new one
6647e2a6151SJulian Elischer 				 * before continuing to search
6657e2a6151SJulian Elischer 				 * for an even better one.
6667e2a6151SJulian Elischer 				 */
667df8bae1dSRodney W. Grimes 				if (ifa_maybe == 0 ||
668df8bae1dSRodney W. Grimes 				    rn_refines((caddr_t)ifa->ifa_netmask,
669df8bae1dSRodney W. Grimes 				    (caddr_t)ifa_maybe->ifa_netmask))
670df8bae1dSRodney W. Grimes 					ifa_maybe = ifa;
671df8bae1dSRodney W. Grimes 			}
672b2af64fdSDavid Greenman 		}
673b2af64fdSDavid Greenman 	}
6740b59d917SJonathan Lemon 	ifa = ifa_maybe;
6750b59d917SJonathan Lemon done:
6760b59d917SJonathan Lemon 	return (ifa);
677df8bae1dSRodney W. Grimes }
678df8bae1dSRodney W. Grimes 
679df8bae1dSRodney W. Grimes /*
680df8bae1dSRodney W. Grimes  * Find an interface address specific to an interface best matching
681df8bae1dSRodney W. Grimes  * a given address.
682df8bae1dSRodney W. Grimes  */
683df8bae1dSRodney W. Grimes struct ifaddr *
684df8bae1dSRodney W. Grimes ifaof_ifpforaddr(addr, ifp)
685df8bae1dSRodney W. Grimes 	struct sockaddr *addr;
686df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
687df8bae1dSRodney W. Grimes {
688df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
689df8bae1dSRodney W. Grimes 	register char *cp, *cp2, *cp3;
690df8bae1dSRodney W. Grimes 	register char *cplim;
691df8bae1dSRodney W. Grimes 	struct ifaddr *ifa_maybe = 0;
692df8bae1dSRodney W. Grimes 	u_int af = addr->sa_family;
693df8bae1dSRodney W. Grimes 
694df8bae1dSRodney W. Grimes 	if (af >= AF_MAX)
695df8bae1dSRodney W. Grimes 		return (0);
69637d40066SPoul-Henning Kamp 	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
697df8bae1dSRodney W. Grimes 		if (ifa->ifa_addr->sa_family != af)
698df8bae1dSRodney W. Grimes 			continue;
699381dd1d2SJulian Elischer 		if (ifa_maybe == 0)
700df8bae1dSRodney W. Grimes 			ifa_maybe = ifa;
701df8bae1dSRodney W. Grimes 		if (ifa->ifa_netmask == 0) {
702df8bae1dSRodney W. Grimes 			if (equal(addr, ifa->ifa_addr) ||
703df8bae1dSRodney W. Grimes 			    (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
704df8bae1dSRodney W. Grimes 				return (ifa);
705df8bae1dSRodney W. Grimes 			continue;
706df8bae1dSRodney W. Grimes 		}
707b2af64fdSDavid Greenman 		if (ifp->if_flags & IFF_POINTOPOINT) {
708b2af64fdSDavid Greenman 			if (equal(addr, ifa->ifa_dstaddr))
709b2af64fdSDavid Greenman 				return (ifa);
7103740e2adSDavid Greenman 		} else {
711df8bae1dSRodney W. Grimes 			cp = addr->sa_data;
712df8bae1dSRodney W. Grimes 			cp2 = ifa->ifa_addr->sa_data;
713df8bae1dSRodney W. Grimes 			cp3 = ifa->ifa_netmask->sa_data;
714df8bae1dSRodney W. Grimes 			cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
715df8bae1dSRodney W. Grimes 			for (; cp3 < cplim; cp3++)
716df8bae1dSRodney W. Grimes 				if ((*cp++ ^ *cp2++) & *cp3)
717df8bae1dSRodney W. Grimes 					break;
718df8bae1dSRodney W. Grimes 			if (cp3 == cplim)
719df8bae1dSRodney W. Grimes 				return (ifa);
720df8bae1dSRodney W. Grimes 		}
721b2af64fdSDavid Greenman 	}
722df8bae1dSRodney W. Grimes 	return (ifa_maybe);
723df8bae1dSRodney W. Grimes }
724df8bae1dSRodney W. Grimes 
725df8bae1dSRodney W. Grimes #include <net/route.h>
726df8bae1dSRodney W. Grimes 
727df8bae1dSRodney W. Grimes /*
728df8bae1dSRodney W. Grimes  * Default action when installing a route with a Link Level gateway.
729df8bae1dSRodney W. Grimes  * Lookup an appropriate real ifa to point to.
730df8bae1dSRodney W. Grimes  * This should be moved to /sys/net/link.c eventually.
731df8bae1dSRodney W. Grimes  */
7323bda9f9bSPoul-Henning Kamp static void
733df8bae1dSRodney W. Grimes link_rtrequest(cmd, rt, sa)
734df8bae1dSRodney W. Grimes 	int cmd;
735df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
736df8bae1dSRodney W. Grimes 	struct sockaddr *sa;
737df8bae1dSRodney W. Grimes {
738df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
739df8bae1dSRodney W. Grimes 	struct sockaddr *dst;
740df8bae1dSRodney W. Grimes 	struct ifnet *ifp;
741df8bae1dSRodney W. Grimes 
742df8bae1dSRodney W. Grimes 	if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) ||
743df8bae1dSRodney W. Grimes 	    ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0))
744df8bae1dSRodney W. Grimes 		return;
7459448326fSPoul-Henning Kamp 	ifa = ifaof_ifpforaddr(dst, ifp);
7469448326fSPoul-Henning Kamp 	if (ifa) {
747df8bae1dSRodney W. Grimes 		IFAFREE(rt->rt_ifa);
748df8bae1dSRodney W. Grimes 		rt->rt_ifa = ifa;
749df8bae1dSRodney W. Grimes 		ifa->ifa_refcnt++;
750df8bae1dSRodney W. Grimes 		if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest)
751df8bae1dSRodney W. Grimes 			ifa->ifa_rtrequest(cmd, rt, sa);
752df8bae1dSRodney W. Grimes 	}
753df8bae1dSRodney W. Grimes }
754df8bae1dSRodney W. Grimes 
755df8bae1dSRodney W. Grimes /*
756df8bae1dSRodney W. Grimes  * Mark an interface down and notify protocols of
757df8bae1dSRodney W. Grimes  * the transition.
758df8bae1dSRodney W. Grimes  * NOTE: must be called at splnet or eqivalent.
759df8bae1dSRodney W. Grimes  */
760df8bae1dSRodney W. Grimes void
761e8c2601dSPoul-Henning Kamp if_unroute(ifp, flag, fam)
762df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
763e8c2601dSPoul-Henning Kamp 	int flag, fam;
764df8bae1dSRodney W. Grimes {
765df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
766df8bae1dSRodney W. Grimes 
767e8c2601dSPoul-Henning Kamp 	ifp->if_flags &= ~flag;
76898b9590eSPoul-Henning Kamp 	getmicrotime(&ifp->if_lastchange);
769e8c2601dSPoul-Henning Kamp 	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
770e8c2601dSPoul-Henning Kamp 		if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family))
771df8bae1dSRodney W. Grimes 			pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
772df8bae1dSRodney W. Grimes 	if_qflush(&ifp->if_snd);
773df8bae1dSRodney W. Grimes 	rt_ifmsg(ifp);
774df8bae1dSRodney W. Grimes }
775df8bae1dSRodney W. Grimes 
776df8bae1dSRodney W. Grimes /*
777df8bae1dSRodney W. Grimes  * Mark an interface up and notify protocols of
778df8bae1dSRodney W. Grimes  * the transition.
779df8bae1dSRodney W. Grimes  * NOTE: must be called at splnet or eqivalent.
780df8bae1dSRodney W. Grimes  */
781df8bae1dSRodney W. Grimes void
782e8c2601dSPoul-Henning Kamp if_route(ifp, flag, fam)
783df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
784e8c2601dSPoul-Henning Kamp 	int flag, fam;
785df8bae1dSRodney W. Grimes {
786176395b2SGarrett Wollman 	register struct ifaddr *ifa;
787df8bae1dSRodney W. Grimes 
788e8c2601dSPoul-Henning Kamp 	ifp->if_flags |= flag;
78998b9590eSPoul-Henning Kamp 	getmicrotime(&ifp->if_lastchange);
790e8c2601dSPoul-Henning Kamp 	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
791e8c2601dSPoul-Henning Kamp 		if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family))
792df8bae1dSRodney W. Grimes 			pfctlinput(PRC_IFUP, ifa->ifa_addr);
793df8bae1dSRodney W. Grimes 	rt_ifmsg(ifp);
79482cd038dSYoshinobu Inoue #ifdef INET6
79582cd038dSYoshinobu Inoue 	in6_if_up(ifp);
79682cd038dSYoshinobu Inoue #endif
797df8bae1dSRodney W. Grimes }
798df8bae1dSRodney W. Grimes 
799df8bae1dSRodney W. Grimes /*
800e8c2601dSPoul-Henning Kamp  * Mark an interface down and notify protocols of
801e8c2601dSPoul-Henning Kamp  * the transition.
802e8c2601dSPoul-Henning Kamp  * NOTE: must be called at splnet or eqivalent.
803e8c2601dSPoul-Henning Kamp  */
804e8c2601dSPoul-Henning Kamp void
805e8c2601dSPoul-Henning Kamp if_down(ifp)
806e8c2601dSPoul-Henning Kamp 	register struct ifnet *ifp;
807e8c2601dSPoul-Henning Kamp {
808e8c2601dSPoul-Henning Kamp 
809e8c2601dSPoul-Henning Kamp 	if_unroute(ifp, IFF_UP, AF_UNSPEC);
810e8c2601dSPoul-Henning Kamp }
811e8c2601dSPoul-Henning Kamp 
812e8c2601dSPoul-Henning Kamp /*
813e8c2601dSPoul-Henning Kamp  * Mark an interface up and notify protocols of
814e8c2601dSPoul-Henning Kamp  * the transition.
815e8c2601dSPoul-Henning Kamp  * NOTE: must be called at splnet or eqivalent.
816e8c2601dSPoul-Henning Kamp  */
817e8c2601dSPoul-Henning Kamp void
818e8c2601dSPoul-Henning Kamp if_up(ifp)
819e8c2601dSPoul-Henning Kamp 	register struct ifnet *ifp;
820e8c2601dSPoul-Henning Kamp {
821e8c2601dSPoul-Henning Kamp 
822e8c2601dSPoul-Henning Kamp 	if_route(ifp, IFF_UP, AF_UNSPEC);
823e8c2601dSPoul-Henning Kamp }
824e8c2601dSPoul-Henning Kamp 
825e8c2601dSPoul-Henning Kamp /*
826df8bae1dSRodney W. Grimes  * Flush an interface queue.
827df8bae1dSRodney W. Grimes  */
8283bda9f9bSPoul-Henning Kamp static void
829df8bae1dSRodney W. Grimes if_qflush(ifq)
830df8bae1dSRodney W. Grimes 	register struct ifqueue *ifq;
831df8bae1dSRodney W. Grimes {
832df8bae1dSRodney W. Grimes 	register struct mbuf *m, *n;
833df8bae1dSRodney W. Grimes 
834df8bae1dSRodney W. Grimes 	n = ifq->ifq_head;
8359448326fSPoul-Henning Kamp 	while ((m = n) != 0) {
836df8bae1dSRodney W. Grimes 		n = m->m_act;
837df8bae1dSRodney W. Grimes 		m_freem(m);
838df8bae1dSRodney W. Grimes 	}
839df8bae1dSRodney W. Grimes 	ifq->ifq_head = 0;
840df8bae1dSRodney W. Grimes 	ifq->ifq_tail = 0;
841df8bae1dSRodney W. Grimes 	ifq->ifq_len = 0;
842df8bae1dSRodney W. Grimes }
843df8bae1dSRodney W. Grimes 
844df8bae1dSRodney W. Grimes /*
845df8bae1dSRodney W. Grimes  * Handle interface watchdog timer routines.  Called
846df8bae1dSRodney W. Grimes  * from softclock, we decrement timers (if set) and
847df8bae1dSRodney W. Grimes  * call the appropriate interface routine on expiration.
848df8bae1dSRodney W. Grimes  */
8493bda9f9bSPoul-Henning Kamp static void
850df8bae1dSRodney W. Grimes if_slowtimo(arg)
851df8bae1dSRodney W. Grimes 	void *arg;
852df8bae1dSRodney W. Grimes {
853df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
854df8bae1dSRodney W. Grimes 	int s = splimp();
855df8bae1dSRodney W. Grimes 
856fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
857df8bae1dSRodney W. Grimes 		if (ifp->if_timer == 0 || --ifp->if_timer)
858df8bae1dSRodney W. Grimes 			continue;
859df8bae1dSRodney W. Grimes 		if (ifp->if_watchdog)
8604a5f1499SDavid Greenman 			(*ifp->if_watchdog)(ifp);
861df8bae1dSRodney W. Grimes 	}
862df8bae1dSRodney W. Grimes 	splx(s);
863df8bae1dSRodney W. Grimes 	timeout(if_slowtimo, (void *)0, hz / IFNET_SLOWHZ);
864df8bae1dSRodney W. Grimes }
865df8bae1dSRodney W. Grimes 
866df8bae1dSRodney W. Grimes /*
867df8bae1dSRodney W. Grimes  * Map interface name to
868df8bae1dSRodney W. Grimes  * interface structure pointer.
869df8bae1dSRodney W. Grimes  */
870df8bae1dSRodney W. Grimes struct ifnet *
87130aad87dSBrooks Davis ifunit(const char *name)
872df8bae1dSRodney W. Grimes {
87301f0fef3SJulian Elischer 	char namebuf[IFNAMSIZ + 1];
87430aad87dSBrooks Davis 	const char *cp;
8758b7805e4SBoris Popov 	struct ifnet *ifp;
876df8bae1dSRodney W. Grimes 	int unit;
8778b7805e4SBoris Popov 	unsigned len, m;
8788b7805e4SBoris Popov 	char c;
879df8bae1dSRodney W. Grimes 
8808b7805e4SBoris Popov 	len = strlen(name);
8818b7805e4SBoris Popov 	if (len < 2 || len > IFNAMSIZ)
8828b7805e4SBoris Popov 		return NULL;
8838b7805e4SBoris Popov 	cp = name + len - 1;
8848b7805e4SBoris Popov 	c = *cp;
8858b7805e4SBoris Popov 	if (c < '0' || c > '9')
8868b7805e4SBoris Popov 		return NULL;		/* trailing garbage */
8878b7805e4SBoris Popov 	unit = 0;
8888b7805e4SBoris Popov 	m = 1;
8898b7805e4SBoris Popov 	do {
8908b7805e4SBoris Popov 		if (cp == name)
8918b7805e4SBoris Popov 			return NULL;	/* no interface name */
8928b7805e4SBoris Popov 		unit += (c - '0') * m;
8938b7805e4SBoris Popov 		if (unit > 1000000)
8948b7805e4SBoris Popov 			return NULL;	/* number is unreasonable */
8958b7805e4SBoris Popov 		m *= 10;
8968b7805e4SBoris Popov 		c = *--cp;
8978b7805e4SBoris Popov 	} while (c >= '0' && c <= '9');
898df8bae1dSRodney W. Grimes 	len = cp - name + 1;
8998b7805e4SBoris Popov 	bcopy(name, namebuf, len);
9008b7805e4SBoris Popov 	namebuf[len] = '\0';
90101f0fef3SJulian Elischer 	/*
90201f0fef3SJulian Elischer 	 * Now search all the interfaces for this name/number
90301f0fef3SJulian Elischer 	 */
904fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
9058b7805e4SBoris Popov 		if (strcmp(ifp->if_name, namebuf))
906df8bae1dSRodney W. Grimes 			continue;
907df8bae1dSRodney W. Grimes 		if (unit == ifp->if_unit)
908df8bae1dSRodney W. Grimes 			break;
909df8bae1dSRodney W. Grimes 	}
910df8bae1dSRodney W. Grimes 	return (ifp);
911df8bae1dSRodney W. Grimes }
912df8bae1dSRodney W. Grimes 
91382cd038dSYoshinobu Inoue 
91482cd038dSYoshinobu Inoue /*
91582cd038dSYoshinobu Inoue  * Map interface name in a sockaddr_dl to
91682cd038dSYoshinobu Inoue  * interface structure pointer.
91782cd038dSYoshinobu Inoue  */
91882cd038dSYoshinobu Inoue struct ifnet *
91982cd038dSYoshinobu Inoue if_withname(sa)
92082cd038dSYoshinobu Inoue 	struct sockaddr *sa;
92182cd038dSYoshinobu Inoue {
92282cd038dSYoshinobu Inoue 	char ifname[IFNAMSIZ+1];
92382cd038dSYoshinobu Inoue 	struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
92482cd038dSYoshinobu Inoue 
92582cd038dSYoshinobu Inoue 	if ( (sa->sa_family != AF_LINK) || (sdl->sdl_nlen == 0) ||
92682cd038dSYoshinobu Inoue 	     (sdl->sdl_nlen > IFNAMSIZ) )
92782cd038dSYoshinobu Inoue 		return NULL;
92882cd038dSYoshinobu Inoue 
92982cd038dSYoshinobu Inoue 	/*
93082cd038dSYoshinobu Inoue 	 * ifunit wants a null-terminated name.  It may not be null-terminated
93182cd038dSYoshinobu Inoue 	 * in the sockaddr.  We don't want to change the caller's sockaddr,
93282cd038dSYoshinobu Inoue 	 * and there might not be room to put the trailing null anyway, so we
93382cd038dSYoshinobu Inoue 	 * make a local copy that we know we can null terminate safely.
93482cd038dSYoshinobu Inoue 	 */
93582cd038dSYoshinobu Inoue 
93682cd038dSYoshinobu Inoue 	bcopy(sdl->sdl_data, ifname, sdl->sdl_nlen);
93782cd038dSYoshinobu Inoue 	ifname[sdl->sdl_nlen] = '\0';
93882cd038dSYoshinobu Inoue 	return ifunit(ifname);
93982cd038dSYoshinobu Inoue }
94082cd038dSYoshinobu Inoue 
94182cd038dSYoshinobu Inoue 
942df8bae1dSRodney W. Grimes /*
943df8bae1dSRodney W. Grimes  * Interface ioctls.
944df8bae1dSRodney W. Grimes  */
945df8bae1dSRodney W. Grimes int
946df8bae1dSRodney W. Grimes ifioctl(so, cmd, data, p)
947df8bae1dSRodney W. Grimes 	struct socket *so;
948ecbb00a2SDoug Rabson 	u_long cmd;
949df8bae1dSRodney W. Grimes 	caddr_t data;
950df8bae1dSRodney W. Grimes 	struct proc *p;
951df8bae1dSRodney W. Grimes {
952df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
953df8bae1dSRodney W. Grimes 	register struct ifreq *ifr;
954413dd0baSPoul-Henning Kamp 	struct ifstat *ifs;
955df8bae1dSRodney W. Grimes 	int error;
95682cd038dSYoshinobu Inoue 	short oif_flags;
957df8bae1dSRodney W. Grimes 
958df8bae1dSRodney W. Grimes 	switch (cmd) {
959df8bae1dSRodney W. Grimes 
960df8bae1dSRodney W. Grimes 	case SIOCGIFCONF:
961df8bae1dSRodney W. Grimes 	case OSIOCGIFCONF:
962df8bae1dSRodney W. Grimes 		return (ifconf(cmd, data));
963df8bae1dSRodney W. Grimes 	}
964df8bae1dSRodney W. Grimes 	ifr = (struct ifreq *)data;
96530aad87dSBrooks Davis 
96630aad87dSBrooks Davis 	switch (cmd) {
96730aad87dSBrooks Davis 	case SIOCIFCREATE:
96830aad87dSBrooks Davis 	case SIOCIFDESTROY:
96930aad87dSBrooks Davis 		if ((error = suser(p)) != 0)
97030aad87dSBrooks Davis 			return (error);
97130aad87dSBrooks Davis 		return ((cmd == SIOCIFCREATE) ?
97230aad87dSBrooks Davis 			if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name)) :
97330aad87dSBrooks Davis 			if_clone_destroy(ifr->ifr_name));
97430aad87dSBrooks Davis 
97530aad87dSBrooks Davis 	case SIOCIFGCLONERS:
97630aad87dSBrooks Davis 		return (if_clone_list((struct if_clonereq *)data));
97730aad87dSBrooks Davis 	}
97830aad87dSBrooks Davis 
979df8bae1dSRodney W. Grimes 	ifp = ifunit(ifr->ifr_name);
980df8bae1dSRodney W. Grimes 	if (ifp == 0)
981df8bae1dSRodney W. Grimes 		return (ENXIO);
982df8bae1dSRodney W. Grimes 	switch (cmd) {
983df8bae1dSRodney W. Grimes 
984df8bae1dSRodney W. Grimes 	case SIOCGIFFLAGS:
985df8bae1dSRodney W. Grimes 		ifr->ifr_flags = ifp->if_flags;
986df8bae1dSRodney W. Grimes 		break;
987df8bae1dSRodney W. Grimes 
988df8bae1dSRodney W. Grimes 	case SIOCGIFMETRIC:
989df8bae1dSRodney W. Grimes 		ifr->ifr_metric = ifp->if_metric;
990df8bae1dSRodney W. Grimes 		break;
991df8bae1dSRodney W. Grimes 
992a7028af7SDavid Greenman 	case SIOCGIFMTU:
993a7028af7SDavid Greenman 		ifr->ifr_mtu = ifp->if_mtu;
994a7028af7SDavid Greenman 		break;
995a7028af7SDavid Greenman 
996074c4a4eSGarrett Wollman 	case SIOCGIFPHYS:
997074c4a4eSGarrett Wollman 		ifr->ifr_phys = ifp->if_physical;
998074c4a4eSGarrett Wollman 		break;
999074c4a4eSGarrett Wollman 
1000df8bae1dSRodney W. Grimes 	case SIOCSIFFLAGS:
1001f711d546SPoul-Henning Kamp 		error = suser(p);
10029448326fSPoul-Henning Kamp 		if (error)
1003df8bae1dSRodney W. Grimes 			return (error);
10044add131eSPoul-Henning Kamp 		ifr->ifr_prevflags = ifp->if_flags;
1005cf4b9371SPoul-Henning Kamp 		if (ifp->if_flags & IFF_SMART) {
1006cf4b9371SPoul-Henning Kamp 			/* Smart drivers twiddle their own routes */
10072f55ead7SPoul-Henning Kamp 		} else if (ifp->if_flags & IFF_UP &&
1008cf4b9371SPoul-Henning Kamp 		    (ifr->ifr_flags & IFF_UP) == 0) {
1009df8bae1dSRodney W. Grimes 			int s = splimp();
1010df8bae1dSRodney W. Grimes 			if_down(ifp);
1011df8bae1dSRodney W. Grimes 			splx(s);
1012cf4b9371SPoul-Henning Kamp 		} else if (ifr->ifr_flags & IFF_UP &&
1013cf4b9371SPoul-Henning Kamp 		    (ifp->if_flags & IFF_UP) == 0) {
1014df8bae1dSRodney W. Grimes 			int s = splimp();
1015df8bae1dSRodney W. Grimes 			if_up(ifp);
1016df8bae1dSRodney W. Grimes 			splx(s);
1017df8bae1dSRodney W. Grimes 		}
1018df8bae1dSRodney W. Grimes 		ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
1019df8bae1dSRodney W. Grimes 			(ifr->ifr_flags &~ IFF_CANTCHANGE);
1020df8bae1dSRodney W. Grimes 		if (ifp->if_ioctl)
1021df8bae1dSRodney W. Grimes 			(void) (*ifp->if_ioctl)(ifp, cmd, data);
102298b9590eSPoul-Henning Kamp 		getmicrotime(&ifp->if_lastchange);
1023df8bae1dSRodney W. Grimes 		break;
1024df8bae1dSRodney W. Grimes 
1025df8bae1dSRodney W. Grimes 	case SIOCSIFMETRIC:
1026f711d546SPoul-Henning Kamp 		error = suser(p);
10279448326fSPoul-Henning Kamp 		if (error)
1028df8bae1dSRodney W. Grimes 			return (error);
1029df8bae1dSRodney W. Grimes 		ifp->if_metric = ifr->ifr_metric;
103098b9590eSPoul-Henning Kamp 		getmicrotime(&ifp->if_lastchange);
1031df8bae1dSRodney W. Grimes 		break;
1032df8bae1dSRodney W. Grimes 
1033074c4a4eSGarrett Wollman 	case SIOCSIFPHYS:
1034f711d546SPoul-Henning Kamp 		error = suser(p);
1035e39a0280SGary Palmer 		if (error)
1036e39a0280SGary Palmer 			return error;
1037e39a0280SGary Palmer 		if (!ifp->if_ioctl)
1038e39a0280SGary Palmer 		        return EOPNOTSUPP;
1039e39a0280SGary Palmer 		error = (*ifp->if_ioctl)(ifp, cmd, data);
1040e39a0280SGary Palmer 		if (error == 0)
104198b9590eSPoul-Henning Kamp 			getmicrotime(&ifp->if_lastchange);
1042e39a0280SGary Palmer 		return(error);
1043074c4a4eSGarrett Wollman 
1044a7028af7SDavid Greenman 	case SIOCSIFMTU:
104582cd038dSYoshinobu Inoue 	{
104682cd038dSYoshinobu Inoue 		u_long oldmtu = ifp->if_mtu;
104782cd038dSYoshinobu Inoue 
1048f711d546SPoul-Henning Kamp 		error = suser(p);
10499448326fSPoul-Henning Kamp 		if (error)
1050a7028af7SDavid Greenman 			return (error);
1051a7028af7SDavid Greenman 		if (ifp->if_ioctl == NULL)
1052a7028af7SDavid Greenman 			return (EOPNOTSUPP);
1053aab3beeeSBrian Somers 		if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU)
105475ee03cbSDavid Greenman 			return (EINVAL);
1055e39a0280SGary Palmer 		error = (*ifp->if_ioctl)(ifp, cmd, data);
105648f71763SRuslan Ermilov 		if (error == 0) {
105798b9590eSPoul-Henning Kamp 			getmicrotime(&ifp->if_lastchange);
105848f71763SRuslan Ermilov 			rt_ifmsg(ifp);
105948f71763SRuslan Ermilov 		}
106082cd038dSYoshinobu Inoue 		/*
106182cd038dSYoshinobu Inoue 		 * If the link MTU changed, do network layer specific procedure.
106282cd038dSYoshinobu Inoue 		 */
106382cd038dSYoshinobu Inoue 		if (ifp->if_mtu != oldmtu) {
106482cd038dSYoshinobu Inoue #ifdef INET6
106582cd038dSYoshinobu Inoue 			nd6_setmtu(ifp);
106682cd038dSYoshinobu Inoue #endif
106782cd038dSYoshinobu Inoue 		}
1068e39a0280SGary Palmer 		return (error);
106982cd038dSYoshinobu Inoue 	}
1070a7028af7SDavid Greenman 
1071df8bae1dSRodney W. Grimes 	case SIOCADDMULTI:
1072df8bae1dSRodney W. Grimes 	case SIOCDELMULTI:
1073f711d546SPoul-Henning Kamp 		error = suser(p);
10749448326fSPoul-Henning Kamp 		if (error)
1075df8bae1dSRodney W. Grimes 			return (error);
1076477180fbSGarrett Wollman 
1077477180fbSGarrett Wollman 		/* Don't allow group membership on non-multicast interfaces. */
1078477180fbSGarrett Wollman 		if ((ifp->if_flags & IFF_MULTICAST) == 0)
1079477180fbSGarrett Wollman 			return EOPNOTSUPP;
1080477180fbSGarrett Wollman 
1081477180fbSGarrett Wollman 		/* Don't let users screw up protocols' entries. */
1082477180fbSGarrett Wollman 		if (ifr->ifr_addr.sa_family != AF_LINK)
1083477180fbSGarrett Wollman 			return EINVAL;
1084477180fbSGarrett Wollman 
1085477180fbSGarrett Wollman 		if (cmd == SIOCADDMULTI) {
1086477180fbSGarrett Wollman 			struct ifmultiaddr *ifma;
1087477180fbSGarrett Wollman 			error = if_addmulti(ifp, &ifr->ifr_addr, &ifma);
1088477180fbSGarrett Wollman 		} else {
1089477180fbSGarrett Wollman 			error = if_delmulti(ifp, &ifr->ifr_addr);
1090477180fbSGarrett Wollman 		}
1091e39a0280SGary Palmer 		if (error == 0)
109298b9590eSPoul-Henning Kamp 			getmicrotime(&ifp->if_lastchange);
1093477180fbSGarrett Wollman 		return error;
1094df8bae1dSRodney W. Grimes 
109541b3e8e5SJun-ichiro itojun Hagino 	case SIOCSIFPHYADDR:
109641b3e8e5SJun-ichiro itojun Hagino 	case SIOCDIFPHYADDR:
109741b3e8e5SJun-ichiro itojun Hagino #ifdef INET6
109841b3e8e5SJun-ichiro itojun Hagino 	case SIOCSIFPHYADDR_IN6:
109941b3e8e5SJun-ichiro itojun Hagino #endif
110033841545SHajimu UMEMOTO 	case SIOCSLIFPHYADDR:
1101a912e453SPeter Wemm         case SIOCSIFMEDIA:
1102d7189ec6SJoerg Wunsch 	case SIOCSIFGENERIC:
1103f711d546SPoul-Henning Kamp 		error = suser(p);
1104a912e453SPeter Wemm 		if (error)
1105a912e453SPeter Wemm 			return (error);
1106a912e453SPeter Wemm 		if (ifp->if_ioctl == 0)
1107a912e453SPeter Wemm 			return (EOPNOTSUPP);
1108a912e453SPeter Wemm 		error = (*ifp->if_ioctl)(ifp, cmd, data);
1109a912e453SPeter Wemm 		if (error == 0)
111098b9590eSPoul-Henning Kamp 			getmicrotime(&ifp->if_lastchange);
1111a912e453SPeter Wemm 		return error;
1112a912e453SPeter Wemm 
1113413dd0baSPoul-Henning Kamp 	case SIOCGIFSTATUS:
1114413dd0baSPoul-Henning Kamp 		ifs = (struct ifstat *)data;
1115413dd0baSPoul-Henning Kamp 		ifs->ascii[0] = '\0';
1116413dd0baSPoul-Henning Kamp 
111733841545SHajimu UMEMOTO 	case SIOCGIFPSRCADDR:
111833841545SHajimu UMEMOTO 	case SIOCGIFPDSTADDR:
111933841545SHajimu UMEMOTO 	case SIOCGLIFPHYADDR:
1120a912e453SPeter Wemm 	case SIOCGIFMEDIA:
1121d7189ec6SJoerg Wunsch 	case SIOCGIFGENERIC:
1122a912e453SPeter Wemm 		if (ifp->if_ioctl == 0)
1123a912e453SPeter Wemm 			return (EOPNOTSUPP);
1124a912e453SPeter Wemm 		return ((*ifp->if_ioctl)(ifp, cmd, data));
1125a912e453SPeter Wemm 
1126b106252cSBill Paul 	case SIOCSIFLLADDR:
1127b106252cSBill Paul 		error = suser(p);
1128b106252cSBill Paul 		if (error)
1129b106252cSBill Paul 			return (error);
113066ce51ceSArchie Cobbs 		return if_setlladdr(ifp,
113166ce51ceSArchie Cobbs 		    ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len);
113266ce51ceSArchie Cobbs 
1133df8bae1dSRodney W. Grimes 	default:
113482cd038dSYoshinobu Inoue 		oif_flags = ifp->if_flags;
1135df8bae1dSRodney W. Grimes 		if (so->so_proto == 0)
1136df8bae1dSRodney W. Grimes 			return (EOPNOTSUPP);
1137df8bae1dSRodney W. Grimes #ifndef COMPAT_43
113882cd038dSYoshinobu Inoue 		error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd,
11392c37256eSGarrett Wollman 								 data,
1140bc6d9b80SJoerg Wunsch 								 ifp, p));
1141df8bae1dSRodney W. Grimes #else
1142df8bae1dSRodney W. Grimes 	    {
1143df8bae1dSRodney W. Grimes 		int ocmd = cmd;
1144df8bae1dSRodney W. Grimes 
1145df8bae1dSRodney W. Grimes 		switch (cmd) {
1146df8bae1dSRodney W. Grimes 
1147df8bae1dSRodney W. Grimes 		case SIOCSIFDSTADDR:
1148df8bae1dSRodney W. Grimes 		case SIOCSIFADDR:
1149df8bae1dSRodney W. Grimes 		case SIOCSIFBRDADDR:
1150df8bae1dSRodney W. Grimes 		case SIOCSIFNETMASK:
1151df8bae1dSRodney W. Grimes #if BYTE_ORDER != BIG_ENDIAN
1152df8bae1dSRodney W. Grimes 			if (ifr->ifr_addr.sa_family == 0 &&
1153df8bae1dSRodney W. Grimes 			    ifr->ifr_addr.sa_len < 16) {
1154df8bae1dSRodney W. Grimes 				ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len;
1155df8bae1dSRodney W. Grimes 				ifr->ifr_addr.sa_len = 16;
1156df8bae1dSRodney W. Grimes 			}
1157df8bae1dSRodney W. Grimes #else
1158df8bae1dSRodney W. Grimes 			if (ifr->ifr_addr.sa_len == 0)
1159df8bae1dSRodney W. Grimes 				ifr->ifr_addr.sa_len = 16;
1160df8bae1dSRodney W. Grimes #endif
1161df8bae1dSRodney W. Grimes 			break;
1162df8bae1dSRodney W. Grimes 
1163df8bae1dSRodney W. Grimes 		case OSIOCGIFADDR:
1164df8bae1dSRodney W. Grimes 			cmd = SIOCGIFADDR;
1165df8bae1dSRodney W. Grimes 			break;
1166df8bae1dSRodney W. Grimes 
1167df8bae1dSRodney W. Grimes 		case OSIOCGIFDSTADDR:
1168df8bae1dSRodney W. Grimes 			cmd = SIOCGIFDSTADDR;
1169df8bae1dSRodney W. Grimes 			break;
1170df8bae1dSRodney W. Grimes 
1171df8bae1dSRodney W. Grimes 		case OSIOCGIFBRDADDR:
1172df8bae1dSRodney W. Grimes 			cmd = SIOCGIFBRDADDR;
1173df8bae1dSRodney W. Grimes 			break;
1174df8bae1dSRodney W. Grimes 
1175df8bae1dSRodney W. Grimes 		case OSIOCGIFNETMASK:
1176df8bae1dSRodney W. Grimes 			cmd = SIOCGIFNETMASK;
1177df8bae1dSRodney W. Grimes 		}
11782c37256eSGarrett Wollman 		error =  ((*so->so_proto->pr_usrreqs->pru_control)(so,
11792c37256eSGarrett Wollman 								   cmd,
11802c37256eSGarrett Wollman 								   data,
1181a29f300eSGarrett Wollman 								   ifp, p));
1182df8bae1dSRodney W. Grimes 		switch (ocmd) {
1183df8bae1dSRodney W. Grimes 
1184df8bae1dSRodney W. Grimes 		case OSIOCGIFADDR:
1185df8bae1dSRodney W. Grimes 		case OSIOCGIFDSTADDR:
1186df8bae1dSRodney W. Grimes 		case OSIOCGIFBRDADDR:
1187df8bae1dSRodney W. Grimes 		case OSIOCGIFNETMASK:
1188df8bae1dSRodney W. Grimes 			*(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family;
118982cd038dSYoshinobu Inoue 
119082cd038dSYoshinobu Inoue 		}
119182cd038dSYoshinobu Inoue 	    }
119282cd038dSYoshinobu Inoue #endif /* COMPAT_43 */
119382cd038dSYoshinobu Inoue 
119482cd038dSYoshinobu Inoue 		if ((oif_flags ^ ifp->if_flags) & IFF_UP) {
119582cd038dSYoshinobu Inoue #ifdef INET6
11963411310dSYoshinobu Inoue 			DELAY(100);/* XXX: temporal workaround for fxp issue*/
119782cd038dSYoshinobu Inoue 			if (ifp->if_flags & IFF_UP) {
119882cd038dSYoshinobu Inoue 				int s = splimp();
119982cd038dSYoshinobu Inoue 				in6_if_up(ifp);
120082cd038dSYoshinobu Inoue 				splx(s);
120182cd038dSYoshinobu Inoue 			}
120282cd038dSYoshinobu Inoue #endif
1203df8bae1dSRodney W. Grimes 		}
1204df8bae1dSRodney W. Grimes 		return (error);
1205df8bae1dSRodney W. Grimes 
1206df8bae1dSRodney W. Grimes 	}
1207df8bae1dSRodney W. Grimes 	return (0);
1208df8bae1dSRodney W. Grimes }
1209df8bae1dSRodney W. Grimes 
1210df8bae1dSRodney W. Grimes /*
1211963e4c2aSGarrett Wollman  * Set/clear promiscuous mode on interface ifp based on the truth value
1212963e4c2aSGarrett Wollman  * of pswitch.  The calls are reference counted so that only the first
1213963e4c2aSGarrett Wollman  * "on" request actually has an effect, as does the final "off" request.
1214963e4c2aSGarrett Wollman  * Results are undefined if the "off" and "on" requests are not matched.
1215963e4c2aSGarrett Wollman  */
1216963e4c2aSGarrett Wollman int
1217963e4c2aSGarrett Wollman ifpromisc(ifp, pswitch)
1218963e4c2aSGarrett Wollman 	struct ifnet *ifp;
1219963e4c2aSGarrett Wollman 	int pswitch;
1220963e4c2aSGarrett Wollman {
1221963e4c2aSGarrett Wollman 	struct ifreq ifr;
12224a26224cSGarrett Wollman 	int error;
12234f3c11a6SBill Fenner 	int oldflags, oldpcount;
1224963e4c2aSGarrett Wollman 
12254f3c11a6SBill Fenner 	oldpcount = ifp->if_pcount;
12262c514a31SBrian Somers 	oldflags = ifp->if_flags;
1227963e4c2aSGarrett Wollman 	if (pswitch) {
1228963e4c2aSGarrett Wollman 		/*
1229963e4c2aSGarrett Wollman 		 * If the device is not configured up, we cannot put it in
1230963e4c2aSGarrett Wollman 		 * promiscuous mode.
1231963e4c2aSGarrett Wollman 		 */
1232963e4c2aSGarrett Wollman 		if ((ifp->if_flags & IFF_UP) == 0)
1233963e4c2aSGarrett Wollman 			return (ENETDOWN);
1234963e4c2aSGarrett Wollman 		if (ifp->if_pcount++ != 0)
1235963e4c2aSGarrett Wollman 			return (0);
1236963e4c2aSGarrett Wollman 		ifp->if_flags |= IFF_PROMISC;
1237963e4c2aSGarrett Wollman 	} else {
1238963e4c2aSGarrett Wollman 		if (--ifp->if_pcount > 0)
1239963e4c2aSGarrett Wollman 			return (0);
1240963e4c2aSGarrett Wollman 		ifp->if_flags &= ~IFF_PROMISC;
1241963e4c2aSGarrett Wollman 	}
1242963e4c2aSGarrett Wollman 	ifr.ifr_flags = ifp->if_flags;
12434a26224cSGarrett Wollman 	error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr);
12444f3c11a6SBill Fenner 	if (error == 0) {
12454f3c11a6SBill Fenner 		log(LOG_INFO, "%s%d: promiscuous mode %s\n",
12464f3c11a6SBill Fenner 		    ifp->if_name, ifp->if_unit,
12474f3c11a6SBill Fenner 		    (ifp->if_flags & IFF_PROMISC) ? "enabled" : "disabled");
12484a26224cSGarrett Wollman 		rt_ifmsg(ifp);
12494f3c11a6SBill Fenner 	} else {
12504f3c11a6SBill Fenner 		ifp->if_pcount = oldpcount;
12512c514a31SBrian Somers 		ifp->if_flags = oldflags;
12524f3c11a6SBill Fenner 	}
12534a26224cSGarrett Wollman 	return error;
1254963e4c2aSGarrett Wollman }
1255963e4c2aSGarrett Wollman 
1256963e4c2aSGarrett Wollman /*
1257df8bae1dSRodney W. Grimes  * Return interface configuration
1258df8bae1dSRodney W. Grimes  * of system.  List may be used
1259df8bae1dSRodney W. Grimes  * in later ioctl's (above) to get
1260df8bae1dSRodney W. Grimes  * other information.
1261df8bae1dSRodney W. Grimes  */
1262df8bae1dSRodney W. Grimes /*ARGSUSED*/
12633bda9f9bSPoul-Henning Kamp static int
1264df8bae1dSRodney W. Grimes ifconf(cmd, data)
1265ecbb00a2SDoug Rabson 	u_long cmd;
1266df8bae1dSRodney W. Grimes 	caddr_t data;
1267df8bae1dSRodney W. Grimes {
12680b59d917SJonathan Lemon 	struct ifconf *ifc = (struct ifconf *)data;
12690b59d917SJonathan Lemon 	struct ifnet *ifp;
12700b59d917SJonathan Lemon 	struct ifaddr *ifa;
1271df8bae1dSRodney W. Grimes 	struct ifreq ifr, *ifrp;
1272df8bae1dSRodney W. Grimes 	int space = ifc->ifc_len, error = 0;
1273df8bae1dSRodney W. Grimes 
1274df8bae1dSRodney W. Grimes 	ifrp = ifc->ifc_req;
12750b59d917SJonathan Lemon 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
12761ce9bf88SPoul-Henning Kamp 		char workbuf[64];
127775c13541SPoul-Henning Kamp 		int ifnlen, addrs;
12782624cf89SGarrett Wollman 
12790b59d917SJonathan Lemon 		if (space > sizeof(ifr))
12800b59d917SJonathan Lemon 			break;
12810b59d917SJonathan Lemon 
12822127f260SArchie Cobbs 		ifnlen = snprintf(workbuf, sizeof(workbuf),
12832127f260SArchie Cobbs 		    "%s%d", ifp->if_name, ifp->if_unit);
12841ce9bf88SPoul-Henning Kamp 		if(ifnlen + 1 > sizeof ifr.ifr_name) {
12852624cf89SGarrett Wollman 			error = ENAMETOOLONG;
1286b3f1e629SGuido van Rooij 			break;
12872624cf89SGarrett Wollman 		} else {
12881ce9bf88SPoul-Henning Kamp 			strcpy(ifr.ifr_name, workbuf);
12892624cf89SGarrett Wollman 		}
12902624cf89SGarrett Wollman 
129175c13541SPoul-Henning Kamp 		addrs = 0;
129222f29826SPoul-Henning Kamp 		ifa = TAILQ_FIRST(&ifp->if_addrhead);
129359562606SGarrett Wollman 		for ( ; space > sizeof (ifr) && ifa;
129422f29826SPoul-Henning Kamp 		    ifa = TAILQ_NEXT(ifa, ifa_link)) {
1295df8bae1dSRodney W. Grimes 			register struct sockaddr *sa = ifa->ifa_addr;
129691421ba2SRobert Watson 			if (jailed(curproc->p_ucred) &&
129791421ba2SRobert Watson 			    prison_if(curproc->p_ucred, sa))
129875c13541SPoul-Henning Kamp 				continue;
129975c13541SPoul-Henning Kamp 			addrs++;
1300df8bae1dSRodney W. Grimes #ifdef COMPAT_43
1301df8bae1dSRodney W. Grimes 			if (cmd == OSIOCGIFCONF) {
1302df8bae1dSRodney W. Grimes 				struct osockaddr *osa =
1303df8bae1dSRodney W. Grimes 					 (struct osockaddr *)&ifr.ifr_addr;
1304df8bae1dSRodney W. Grimes 				ifr.ifr_addr = *sa;
1305df8bae1dSRodney W. Grimes 				osa->sa_family = sa->sa_family;
1306df8bae1dSRodney W. Grimes 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
1307df8bae1dSRodney W. Grimes 						sizeof (ifr));
1308df8bae1dSRodney W. Grimes 				ifrp++;
1309df8bae1dSRodney W. Grimes 			} else
1310df8bae1dSRodney W. Grimes #endif
1311df8bae1dSRodney W. Grimes 			if (sa->sa_len <= sizeof(*sa)) {
1312df8bae1dSRodney W. Grimes 				ifr.ifr_addr = *sa;
1313df8bae1dSRodney W. Grimes 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
1314df8bae1dSRodney W. Grimes 						sizeof (ifr));
1315df8bae1dSRodney W. Grimes 				ifrp++;
1316df8bae1dSRodney W. Grimes 			} else {
1317d91a068eSGuido van Rooij 				if (space < sizeof (ifr) + sa->sa_len -
1318d91a068eSGuido van Rooij 					    sizeof(*sa))
1319b3f1e629SGuido van Rooij 					break;
1320df8bae1dSRodney W. Grimes 				space -= sa->sa_len - sizeof(*sa);
1321df8bae1dSRodney W. Grimes 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
1322df8bae1dSRodney W. Grimes 						sizeof (ifr.ifr_name));
1323df8bae1dSRodney W. Grimes 				if (error == 0)
1324df8bae1dSRodney W. Grimes 				    error = copyout((caddr_t)sa,
1325df8bae1dSRodney W. Grimes 				      (caddr_t)&ifrp->ifr_addr, sa->sa_len);
1326df8bae1dSRodney W. Grimes 				ifrp = (struct ifreq *)
1327df8bae1dSRodney W. Grimes 					(sa->sa_len + (caddr_t)&ifrp->ifr_addr);
1328df8bae1dSRodney W. Grimes 			}
1329df8bae1dSRodney W. Grimes 			if (error)
1330df8bae1dSRodney W. Grimes 				break;
1331df8bae1dSRodney W. Grimes 			space -= sizeof (ifr);
1332df8bae1dSRodney W. Grimes 		}
1333b3f1e629SGuido van Rooij 		if (error)
1334b3f1e629SGuido van Rooij 			break;
133575c13541SPoul-Henning Kamp 		if (!addrs) {
133675c13541SPoul-Henning Kamp 			bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
133775c13541SPoul-Henning Kamp 			error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
133875c13541SPoul-Henning Kamp 			    sizeof (ifr));
133975c13541SPoul-Henning Kamp 			if (error)
134075c13541SPoul-Henning Kamp 				break;
1341b3f1e629SGuido van Rooij 			space -= sizeof (ifr);
1342b3f1e629SGuido van Rooij 			ifrp++;
134375c13541SPoul-Henning Kamp 		}
1344df8bae1dSRodney W. Grimes 	}
1345df8bae1dSRodney W. Grimes 	ifc->ifc_len -= space;
1346df8bae1dSRodney W. Grimes 	return (error);
1347df8bae1dSRodney W. Grimes }
1348df8bae1dSRodney W. Grimes 
13491158dfb7SGarrett Wollman /*
13501158dfb7SGarrett Wollman  * Just like if_promisc(), but for all-multicast-reception mode.
13511158dfb7SGarrett Wollman  */
13521158dfb7SGarrett Wollman int
13531158dfb7SGarrett Wollman if_allmulti(ifp, onswitch)
13541158dfb7SGarrett Wollman 	struct ifnet *ifp;
13551158dfb7SGarrett Wollman 	int onswitch;
13561158dfb7SGarrett Wollman {
13571158dfb7SGarrett Wollman 	int error = 0;
13581158dfb7SGarrett Wollman 	int s = splimp();
13591158dfb7SGarrett Wollman 
13601158dfb7SGarrett Wollman 	if (onswitch) {
13611158dfb7SGarrett Wollman 		if (ifp->if_amcount++ == 0) {
13621158dfb7SGarrett Wollman 			ifp->if_flags |= IFF_ALLMULTI;
13631158dfb7SGarrett Wollman 			error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0);
13641158dfb7SGarrett Wollman 		}
13651158dfb7SGarrett Wollman 	} else {
13661158dfb7SGarrett Wollman 		if (ifp->if_amcount > 1) {
13671158dfb7SGarrett Wollman 			ifp->if_amcount--;
13681158dfb7SGarrett Wollman 		} else {
13691158dfb7SGarrett Wollman 			ifp->if_amcount = 0;
13701158dfb7SGarrett Wollman 			ifp->if_flags &= ~IFF_ALLMULTI;
13711158dfb7SGarrett Wollman 			error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0);
13721158dfb7SGarrett Wollman 		}
13731158dfb7SGarrett Wollman 	}
13741158dfb7SGarrett Wollman 	splx(s);
13754a26224cSGarrett Wollman 
13764a26224cSGarrett Wollman 	if (error == 0)
13774a26224cSGarrett Wollman 		rt_ifmsg(ifp);
13781158dfb7SGarrett Wollman 	return error;
13791158dfb7SGarrett Wollman }
13801158dfb7SGarrett Wollman 
13811158dfb7SGarrett Wollman /*
13821158dfb7SGarrett Wollman  * Add a multicast listenership to the interface in question.
13831158dfb7SGarrett Wollman  * The link layer provides a routine which converts
13841158dfb7SGarrett Wollman  */
13851158dfb7SGarrett Wollman int
1386373f88edSGarrett Wollman if_addmulti(ifp, sa, retifma)
13871158dfb7SGarrett Wollman 	struct ifnet *ifp;	/* interface to manipulate */
13881158dfb7SGarrett Wollman 	struct sockaddr *sa;	/* address to add */
1389b2053118SGarrett Wollman 	struct ifmultiaddr **retifma;
13901158dfb7SGarrett Wollman {
13911158dfb7SGarrett Wollman 	struct sockaddr *llsa, *dupsa;
13921158dfb7SGarrett Wollman 	int error, s;
13931158dfb7SGarrett Wollman 	struct ifmultiaddr *ifma;
13941158dfb7SGarrett Wollman 
139557af7922SJulian Elischer 	/*
139657af7922SJulian Elischer 	 * If the matching multicast address already exists
139757af7922SJulian Elischer 	 * then don't add a new one, just add a reference
139857af7922SJulian Elischer 	 */
13996817526dSPoul-Henning Kamp 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
140057af7922SJulian Elischer 		if (equal(sa, ifma->ifma_addr)) {
14011158dfb7SGarrett Wollman 			ifma->ifma_refcount++;
140257af7922SJulian Elischer 			if (retifma)
140357af7922SJulian Elischer 				*retifma = ifma;
14041158dfb7SGarrett Wollman 			return 0;
14051158dfb7SGarrett Wollman 		}
140657af7922SJulian Elischer 	}
14071158dfb7SGarrett Wollman 
14081158dfb7SGarrett Wollman 	/*
14091158dfb7SGarrett Wollman 	 * Give the link layer a chance to accept/reject it, and also
14101158dfb7SGarrett Wollman 	 * find out which AF_LINK address this maps to, if it isn't one
14111158dfb7SGarrett Wollman 	 * already.
14121158dfb7SGarrett Wollman 	 */
14131158dfb7SGarrett Wollman 	if (ifp->if_resolvemulti) {
14141158dfb7SGarrett Wollman 		error = ifp->if_resolvemulti(ifp, &llsa, sa);
14151158dfb7SGarrett Wollman 		if (error) return error;
14161158dfb7SGarrett Wollman 	} else {
14171158dfb7SGarrett Wollman 		llsa = 0;
14181158dfb7SGarrett Wollman 	}
14191158dfb7SGarrett Wollman 
14201158dfb7SGarrett Wollman 	MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK);
14211158dfb7SGarrett Wollman 	MALLOC(dupsa, struct sockaddr *, sa->sa_len, M_IFMADDR, M_WAITOK);
14221158dfb7SGarrett Wollman 	bcopy(sa, dupsa, sa->sa_len);
14231158dfb7SGarrett Wollman 
14241158dfb7SGarrett Wollman 	ifma->ifma_addr = dupsa;
14251158dfb7SGarrett Wollman 	ifma->ifma_lladdr = llsa;
14261158dfb7SGarrett Wollman 	ifma->ifma_ifp = ifp;
14271158dfb7SGarrett Wollman 	ifma->ifma_refcount = 1;
1428373f88edSGarrett Wollman 	ifma->ifma_protospec = 0;
1429477180fbSGarrett Wollman 	rt_newmaddrmsg(RTM_NEWMADDR, ifma);
1430373f88edSGarrett Wollman 
14311158dfb7SGarrett Wollman 	/*
14321158dfb7SGarrett Wollman 	 * Some network interfaces can scan the address list at
14331158dfb7SGarrett Wollman 	 * interrupt time; lock them out.
14341158dfb7SGarrett Wollman 	 */
14351158dfb7SGarrett Wollman 	s = splimp();
14366817526dSPoul-Henning Kamp 	TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link);
14371158dfb7SGarrett Wollman 	splx(s);
1438373f88edSGarrett Wollman 	*retifma = ifma;
14391158dfb7SGarrett Wollman 
14401158dfb7SGarrett Wollman 	if (llsa != 0) {
14416817526dSPoul-Henning Kamp 		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
14421158dfb7SGarrett Wollman 			if (equal(ifma->ifma_addr, llsa))
14431158dfb7SGarrett Wollman 				break;
14441158dfb7SGarrett Wollman 		}
14451158dfb7SGarrett Wollman 		if (ifma) {
14461158dfb7SGarrett Wollman 			ifma->ifma_refcount++;
14471158dfb7SGarrett Wollman 		} else {
14481158dfb7SGarrett Wollman 			MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma,
14491158dfb7SGarrett Wollman 			       M_IFMADDR, M_WAITOK);
1450477180fbSGarrett Wollman 			MALLOC(dupsa, struct sockaddr *, llsa->sa_len,
1451477180fbSGarrett Wollman 			       M_IFMADDR, M_WAITOK);
1452477180fbSGarrett Wollman 			bcopy(llsa, dupsa, llsa->sa_len);
1453477180fbSGarrett Wollman 			ifma->ifma_addr = dupsa;
14541158dfb7SGarrett Wollman 			ifma->ifma_ifp = ifp;
14551158dfb7SGarrett Wollman 			ifma->ifma_refcount = 1;
14561158dfb7SGarrett Wollman 			s = splimp();
14576817526dSPoul-Henning Kamp 			TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link);
14581158dfb7SGarrett Wollman 			splx(s);
14591158dfb7SGarrett Wollman 		}
146057af7922SJulian Elischer 	}
14611158dfb7SGarrett Wollman 	/*
14621158dfb7SGarrett Wollman 	 * We are certain we have added something, so call down to the
14631158dfb7SGarrett Wollman 	 * interface to let them know about it.
14641158dfb7SGarrett Wollman 	 */
14651158dfb7SGarrett Wollman 	s = splimp();
14661158dfb7SGarrett Wollman 	ifp->if_ioctl(ifp, SIOCADDMULTI, 0);
14671158dfb7SGarrett Wollman 	splx(s);
14681158dfb7SGarrett Wollman 
14691158dfb7SGarrett Wollman 	return 0;
14701158dfb7SGarrett Wollman }
14711158dfb7SGarrett Wollman 
14721158dfb7SGarrett Wollman /*
14731158dfb7SGarrett Wollman  * Remove a reference to a multicast address on this interface.  Yell
14741158dfb7SGarrett Wollman  * if the request does not match an existing membership.
14751158dfb7SGarrett Wollman  */
14761158dfb7SGarrett Wollman int
14771158dfb7SGarrett Wollman if_delmulti(ifp, sa)
14781158dfb7SGarrett Wollman 	struct ifnet *ifp;
14791158dfb7SGarrett Wollman 	struct sockaddr *sa;
14801158dfb7SGarrett Wollman {
14811158dfb7SGarrett Wollman 	struct ifmultiaddr *ifma;
14821158dfb7SGarrett Wollman 	int s;
14831158dfb7SGarrett Wollman 
14846817526dSPoul-Henning Kamp 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
14851158dfb7SGarrett Wollman 		if (equal(sa, ifma->ifma_addr))
14861158dfb7SGarrett Wollman 			break;
14871158dfb7SGarrett Wollman 	if (ifma == 0)
14881158dfb7SGarrett Wollman 		return ENOENT;
14891158dfb7SGarrett Wollman 
14901158dfb7SGarrett Wollman 	if (ifma->ifma_refcount > 1) {
14911158dfb7SGarrett Wollman 		ifma->ifma_refcount--;
14921158dfb7SGarrett Wollman 		return 0;
14931158dfb7SGarrett Wollman 	}
14941158dfb7SGarrett Wollman 
1495477180fbSGarrett Wollman 	rt_newmaddrmsg(RTM_DELMADDR, ifma);
14961158dfb7SGarrett Wollman 	sa = ifma->ifma_lladdr;
14971158dfb7SGarrett Wollman 	s = splimp();
14986817526dSPoul-Henning Kamp 	TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
1499ccb7cc8dSYaroslav Tykhiy 	/*
1500ccb7cc8dSYaroslav Tykhiy 	 * Make sure the interface driver is notified
1501ccb7cc8dSYaroslav Tykhiy 	 * in the case of a link layer mcast group being left.
1502ccb7cc8dSYaroslav Tykhiy 	 */
1503ccb7cc8dSYaroslav Tykhiy 	if (ifma->ifma_addr->sa_family == AF_LINK && sa == 0)
1504ccb7cc8dSYaroslav Tykhiy 		ifp->if_ioctl(ifp, SIOCDELMULTI, 0);
15051158dfb7SGarrett Wollman 	splx(s);
15061158dfb7SGarrett Wollman 	free(ifma->ifma_addr, M_IFMADDR);
15071158dfb7SGarrett Wollman 	free(ifma, M_IFMADDR);
15081158dfb7SGarrett Wollman 	if (sa == 0)
15091158dfb7SGarrett Wollman 		return 0;
15101158dfb7SGarrett Wollman 
15111158dfb7SGarrett Wollman 	/*
15121158dfb7SGarrett Wollman 	 * Now look for the link-layer address which corresponds to
15131158dfb7SGarrett Wollman 	 * this network address.  It had been squirreled away in
15141158dfb7SGarrett Wollman 	 * ifma->ifma_lladdr for this purpose (so we don't have
15151158dfb7SGarrett Wollman 	 * to call ifp->if_resolvemulti() again), and we saved that
15161158dfb7SGarrett Wollman 	 * value in sa above.  If some nasty deleted the
15171158dfb7SGarrett Wollman 	 * link-layer address out from underneath us, we can deal because
15181158dfb7SGarrett Wollman 	 * the address we stored was is not the same as the one which was
15191158dfb7SGarrett Wollman 	 * in the record for the link-layer address.  (So we don't complain
15201158dfb7SGarrett Wollman 	 * in that case.)
15211158dfb7SGarrett Wollman 	 */
15226817526dSPoul-Henning Kamp 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
15231158dfb7SGarrett Wollman 		if (equal(sa, ifma->ifma_addr))
15241158dfb7SGarrett Wollman 			break;
15251158dfb7SGarrett Wollman 	if (ifma == 0)
15261158dfb7SGarrett Wollman 		return 0;
15271158dfb7SGarrett Wollman 
15281158dfb7SGarrett Wollman 	if (ifma->ifma_refcount > 1) {
15291158dfb7SGarrett Wollman 		ifma->ifma_refcount--;
15301158dfb7SGarrett Wollman 		return 0;
15311158dfb7SGarrett Wollman 	}
15321158dfb7SGarrett Wollman 
15331158dfb7SGarrett Wollman 	s = splimp();
15346817526dSPoul-Henning Kamp 	TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
1535c7323482SBill Paul 	ifp->if_ioctl(ifp, SIOCDELMULTI, 0);
15361158dfb7SGarrett Wollman 	splx(s);
15371158dfb7SGarrett Wollman 	free(ifma->ifma_addr, M_IFMADDR);
15381158dfb7SGarrett Wollman 	free(sa, M_IFMADDR);
15391158dfb7SGarrett Wollman 	free(ifma, M_IFMADDR);
15401158dfb7SGarrett Wollman 
15411158dfb7SGarrett Wollman 	return 0;
15421158dfb7SGarrett Wollman }
15431158dfb7SGarrett Wollman 
154466ce51ceSArchie Cobbs /*
154566ce51ceSArchie Cobbs  * Set the link layer address on an interface.
154666ce51ceSArchie Cobbs  *
154766ce51ceSArchie Cobbs  * At this time we only support certain types of interfaces,
154866ce51ceSArchie Cobbs  * and we don't allow the length of the address to change.
154966ce51ceSArchie Cobbs  */
155066ce51ceSArchie Cobbs int
155166ce51ceSArchie Cobbs if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len)
155266ce51ceSArchie Cobbs {
155366ce51ceSArchie Cobbs 	struct sockaddr_dl *sdl;
155466ce51ceSArchie Cobbs 	struct ifaddr *ifa;
155566ce51ceSArchie Cobbs 
155666ce51ceSArchie Cobbs 	ifa = ifnet_addrs[ifp->if_index - 1];
155766ce51ceSArchie Cobbs 	if (ifa == NULL)
155866ce51ceSArchie Cobbs 		return (EINVAL);
155966ce51ceSArchie Cobbs 	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
156066ce51ceSArchie Cobbs 	if (sdl == NULL)
156166ce51ceSArchie Cobbs 		return (EINVAL);
156266ce51ceSArchie Cobbs 	if (len != sdl->sdl_alen)	/* don't allow length to change */
156366ce51ceSArchie Cobbs 		return (EINVAL);
156466ce51ceSArchie Cobbs 	switch (ifp->if_type) {
156566ce51ceSArchie Cobbs 	case IFT_ETHER:			/* these types use struct arpcom */
156666ce51ceSArchie Cobbs 	case IFT_FDDI:
156766ce51ceSArchie Cobbs 	case IFT_XETHER:
156866ce51ceSArchie Cobbs 	case IFT_ISO88025:
1569b7bffa71SYaroslav Tykhiy 	case IFT_L2VLAN:
157066ce51ceSArchie Cobbs 		bcopy(lladdr, ((struct arpcom *)ifp->if_softc)->ac_enaddr, len);
157166ce51ceSArchie Cobbs 		bcopy(lladdr, LLADDR(sdl), len);
157266ce51ceSArchie Cobbs 		break;
157366ce51ceSArchie Cobbs 	default:
157466ce51ceSArchie Cobbs 		return (ENODEV);
157566ce51ceSArchie Cobbs 	}
157666ce51ceSArchie Cobbs 	/*
157766ce51ceSArchie Cobbs 	 * If the interface is already up, we need
157866ce51ceSArchie Cobbs 	 * to re-init it in order to reprogram its
157966ce51ceSArchie Cobbs 	 * address filter.
158066ce51ceSArchie Cobbs 	 */
158166ce51ceSArchie Cobbs 	if ((ifp->if_flags & IFF_UP) != 0) {
158266ce51ceSArchie Cobbs 		ifp->if_flags &= ~IFF_UP;
158366ce51ceSArchie Cobbs 		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, NULL);
158466ce51ceSArchie Cobbs 		ifp->if_flags |= IFF_UP;
158566ce51ceSArchie Cobbs 		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, NULL);
158666ce51ceSArchie Cobbs 	}
158766ce51ceSArchie Cobbs 	return (0);
158866ce51ceSArchie Cobbs }
158966ce51ceSArchie Cobbs 
1590373f88edSGarrett Wollman struct ifmultiaddr *
1591373f88edSGarrett Wollman ifmaof_ifpforaddr(sa, ifp)
1592373f88edSGarrett Wollman 	struct sockaddr *sa;
1593373f88edSGarrett Wollman 	struct ifnet *ifp;
1594373f88edSGarrett Wollman {
1595373f88edSGarrett Wollman 	struct ifmultiaddr *ifma;
1596373f88edSGarrett Wollman 
15976817526dSPoul-Henning Kamp 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
1598373f88edSGarrett Wollman 		if (equal(ifma->ifma_addr, sa))
1599373f88edSGarrett Wollman 			break;
1600373f88edSGarrett Wollman 
1601373f88edSGarrett Wollman 	return ifma;
1602373f88edSGarrett Wollman }
1603373f88edSGarrett Wollman 
1604602d513cSGarrett Wollman SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers");
16052c37256eSGarrett Wollman SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management");
1606