xref: /freebsd/sys/net/if.c (revision f9132cebdc9244fc547a667aa867def6759c913a)
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);
74f9132cebSJonathan Lemon static void	if_grow(void);
75f9132cebSJonathan Lemon static void	if_init(void *);
76f9132cebSJonathan Lemon static void	if_check(void *);
770b59d917SJonathan Lemon static void	if_qflush(struct ifqueue *);
780b59d917SJonathan Lemon static void	if_slowtimo(void *);
790b59d917SJonathan Lemon static void	link_rtrequest(int, struct rtentry *, struct sockaddr *);
800b59d917SJonathan Lemon static int	if_rtdel(struct radix_node *, void *);
810b59d917SJonathan Lemon static struct	if_clone *if_clone_lookup(const char *, int *);
820b59d917SJonathan Lemon static int	if_clone_list(struct if_clonereq *);
8382cd038dSYoshinobu Inoue #ifdef INET6
8482cd038dSYoshinobu Inoue /*
8582cd038dSYoshinobu Inoue  * XXX: declare here to avoid to include many inet6 related files..
8682cd038dSYoshinobu Inoue  * should be more generalized?
8782cd038dSYoshinobu Inoue  */
8882cd038dSYoshinobu Inoue extern void	nd6_setmtu __P((struct ifnet *));
8982cd038dSYoshinobu Inoue #endif
9082cd038dSYoshinobu Inoue 
910b59d917SJonathan Lemon int	if_index = 0;
92f9132cebSJonathan Lemon struct	ifindex_entry *ifindex_table = NULL;
930b59d917SJonathan Lemon int	ifqmaxlen = IFQ_MAXLEN;
940b59d917SJonathan Lemon struct	ifnethead ifnet;	/* depend on static init XXX */
9530aad87dSBrooks Davis int	if_cloners_count;
960b59d917SJonathan Lemon LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
970b59d917SJonathan Lemon 
98f9132cebSJonathan Lemon static int	if_indexlim = 8;
99f9132cebSJonathan Lemon 
1000b59d917SJonathan Lemon /*
1010b59d917SJonathan Lemon  * System initialization
1020b59d917SJonathan Lemon  */
103f9132cebSJonathan Lemon SYSINIT(interfaces, SI_SUB_INIT_IF, SI_ORDER_FIRST, if_init, NULL)
104f9132cebSJonathan Lemon SYSINIT(interface_check, SI_SUB_PROTO_IF, SI_ORDER_FIRST, if_check, NULL)
1050b59d917SJonathan Lemon 
1060b59d917SJonathan Lemon MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address");
1070b59d917SJonathan Lemon MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address");
10830aad87dSBrooks Davis 
109df8bae1dSRodney W. Grimes /*
110df8bae1dSRodney W. Grimes  * Network interface utility routines.
111df8bae1dSRodney W. Grimes  *
112df8bae1dSRodney W. Grimes  * Routines with ifa_ifwith* names take sockaddr *'s as
113df8bae1dSRodney W. Grimes  * parameters.
114df8bae1dSRodney W. Grimes  */
1152b14f991SJulian Elischer /* ARGSUSED*/
116f9132cebSJonathan Lemon static void
117f9132cebSJonathan Lemon if_init(dummy)
118f9132cebSJonathan Lemon 	void *dummy;
119f9132cebSJonathan Lemon {
120f9132cebSJonathan Lemon 
121f9132cebSJonathan Lemon 	TAILQ_INIT(&ifnet);
122f9132cebSJonathan Lemon 	if_grow();				/* create initial table */
123f9132cebSJonathan Lemon }
124f9132cebSJonathan Lemon 
125f9132cebSJonathan Lemon static void
126f9132cebSJonathan Lemon if_grow(void)
127f9132cebSJonathan Lemon {
128f9132cebSJonathan Lemon 	u_int n;
129f9132cebSJonathan Lemon 	struct ifindex_entry *e;
130f9132cebSJonathan Lemon 
131f9132cebSJonathan Lemon 	if_indexlim <<= 1;
132f9132cebSJonathan Lemon 	n = if_indexlim * sizeof(*e);
133f9132cebSJonathan Lemon 	e = malloc(n, M_IFADDR, M_WAITOK | M_ZERO);
134f9132cebSJonathan Lemon 	if (ifindex_table != NULL) {
135f9132cebSJonathan Lemon 		memcpy((caddr_t)e, (caddr_t)ifindex_table, n/2);
136f9132cebSJonathan Lemon 		free((caddr_t)ifindex_table, M_IFADDR);
137f9132cebSJonathan Lemon 	}
138f9132cebSJonathan Lemon 	ifindex_table = e;
139f9132cebSJonathan Lemon }
140f9132cebSJonathan Lemon 
141f9132cebSJonathan Lemon /* ARGSUSED*/
142f9132cebSJonathan Lemon static void
143f9132cebSJonathan Lemon if_check(dummy)
14427501cb6SBruce Evans 	void *dummy;
145df8bae1dSRodney W. Grimes {
1468ba5bdaeSPeter Wemm 	struct ifnet *ifp;
1478ba5bdaeSPeter Wemm 	int s;
148df8bae1dSRodney W. Grimes 
1498ba5bdaeSPeter Wemm 	s = splimp();
150fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
151e0ea20bcSPoul-Henning Kamp 		if (ifp->if_snd.ifq_maxlen == 0) {
152e0ea20bcSPoul-Henning Kamp 			printf("%s%d XXX: driver didn't set ifq_maxlen\n",
153e0ea20bcSPoul-Henning Kamp 			    ifp->if_name, ifp->if_unit);
154df8bae1dSRodney W. Grimes 			ifp->if_snd.ifq_maxlen = ifqmaxlen;
155e0ea20bcSPoul-Henning Kamp 		}
1565e980e22SJohn Baldwin 		if (!mtx_initialized(&ifp->if_snd.ifq_mtx)) {
157df5e1987SJonathan Lemon 			printf("%s%d XXX: driver didn't initialize queue mtx\n",
158df5e1987SJonathan Lemon 			    ifp->if_name, ifp->if_unit);
159df5e1987SJonathan Lemon 			mtx_init(&ifp->if_snd.ifq_mtx, "unknown", MTX_DEF);
160df5e1987SJonathan Lemon 		}
161df5e1987SJonathan Lemon 	}
1628ba5bdaeSPeter Wemm 	splx(s);
163df8bae1dSRodney W. Grimes 	if_slowtimo(0);
164df8bae1dSRodney W. Grimes }
165df8bae1dSRodney W. Grimes 
166df8bae1dSRodney W. Grimes /*
167df8bae1dSRodney W. Grimes  * Attach an interface to the
168df8bae1dSRodney W. Grimes  * list of "active" interfaces.
169df8bae1dSRodney W. Grimes  */
170df8bae1dSRodney W. Grimes void
171df8bae1dSRodney W. Grimes if_attach(ifp)
172df8bae1dSRodney W. Grimes 	struct ifnet *ifp;
173df8bae1dSRodney W. Grimes {
174df8bae1dSRodney W. Grimes 	unsigned socksize, ifasize;
1751ce9bf88SPoul-Henning Kamp 	int namelen, masklen;
1761ce9bf88SPoul-Henning Kamp 	char workbuf[64];
177df8bae1dSRodney W. Grimes 	register struct sockaddr_dl *sdl;
178df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
179df8bae1dSRodney W. Grimes 
18029412182SGarrett Wollman 	TAILQ_INSERT_TAIL(&ifnet, ifp, if_link);
181df8bae1dSRodney W. Grimes 	ifp->if_index = ++if_index;
18259562606SGarrett Wollman 	/*
18359562606SGarrett Wollman 	 * XXX -
18459562606SGarrett Wollman 	 * The old code would work if the interface passed a pre-existing
18559562606SGarrett Wollman 	 * chain of ifaddrs to this code.  We don't trust our callers to
18659562606SGarrett Wollman 	 * properly initialize the tailq, however, so we no longer allow
18759562606SGarrett Wollman 	 * this unlikely case.
18859562606SGarrett Wollman 	 */
18959562606SGarrett Wollman 	TAILQ_INIT(&ifp->if_addrhead);
19082cd038dSYoshinobu Inoue 	TAILQ_INIT(&ifp->if_prefixhead);
1916817526dSPoul-Henning Kamp 	TAILQ_INIT(&ifp->if_multiaddrs);
19298b9590eSPoul-Henning Kamp 	getmicrotime(&ifp->if_lastchange);
193f9132cebSJonathan Lemon 	if (if_index >= if_indexlim)
194f9132cebSJonathan Lemon 		if_grow();
19582cd038dSYoshinobu Inoue 
196f9132cebSJonathan Lemon 	ifnet_byindex(if_index) = ifp;
197df5e1987SJonathan Lemon 	mtx_init(&ifp->if_snd.ifq_mtx, ifp->if_name, MTX_DEF);
198df5e1987SJonathan Lemon 
199df8bae1dSRodney W. Grimes 	/*
200df8bae1dSRodney W. Grimes 	 * create a Link Level name for this device
201df8bae1dSRodney W. Grimes 	 */
2022127f260SArchie Cobbs 	namelen = snprintf(workbuf, sizeof(workbuf),
2032127f260SArchie Cobbs 	    "%s%d", ifp->if_name, ifp->if_unit);
204df8bae1dSRodney W. Grimes #define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
2051ce9bf88SPoul-Henning Kamp 	masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
206df8bae1dSRodney W. Grimes 	socksize = masklen + ifp->if_addrlen;
207df8bae1dSRodney W. Grimes #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
208df8bae1dSRodney W. Grimes 	if (socksize < sizeof(*sdl))
209df8bae1dSRodney W. Grimes 		socksize = sizeof(*sdl);
2108a261b8fSDoug Rabson 	socksize = ROUNDUP(socksize);
211df8bae1dSRodney W. Grimes 	ifasize = sizeof(*ifa) + 2 * socksize;
2127cc0979fSDavid Malone 	ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK | M_ZERO);
2139448326fSPoul-Henning Kamp 	if (ifa) {
214df8bae1dSRodney W. Grimes 		sdl = (struct sockaddr_dl *)(ifa + 1);
215df8bae1dSRodney W. Grimes 		sdl->sdl_len = socksize;
216df8bae1dSRodney W. Grimes 		sdl->sdl_family = AF_LINK;
2171ce9bf88SPoul-Henning Kamp 		bcopy(workbuf, sdl->sdl_data, namelen);
2181ce9bf88SPoul-Henning Kamp 		sdl->sdl_nlen = namelen;
219df8bae1dSRodney W. Grimes 		sdl->sdl_index = ifp->if_index;
220df8bae1dSRodney W. Grimes 		sdl->sdl_type = ifp->if_type;
221f9132cebSJonathan Lemon 		ifaddr_byindex(if_index) = ifa;
222df8bae1dSRodney W. Grimes 		ifa->ifa_ifp = ifp;
223df8bae1dSRodney W. Grimes 		ifa->ifa_rtrequest = link_rtrequest;
224df8bae1dSRodney W. Grimes 		ifa->ifa_addr = (struct sockaddr *)sdl;
225df8bae1dSRodney W. Grimes 		sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
226df8bae1dSRodney W. Grimes 		ifa->ifa_netmask = (struct sockaddr *)sdl;
227df8bae1dSRodney W. Grimes 		sdl->sdl_len = masklen;
228df8bae1dSRodney W. Grimes 		while (namelen != 0)
229df8bae1dSRodney W. Grimes 			sdl->sdl_data[--namelen] = 0xff;
23059562606SGarrett Wollman 		TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link);
231df8bae1dSRodney W. Grimes 	}
232df8bae1dSRodney W. Grimes }
2336182fdbdSPeter Wemm 
2346182fdbdSPeter Wemm /*
2356182fdbdSPeter Wemm  * Detach an interface, removing it from the
2366182fdbdSPeter Wemm  * list of "active" interfaces.
2376182fdbdSPeter Wemm  */
2386182fdbdSPeter Wemm void
2396182fdbdSPeter Wemm if_detach(ifp)
2406182fdbdSPeter Wemm 	struct ifnet *ifp;
2416182fdbdSPeter Wemm {
2426182fdbdSPeter Wemm 	struct ifaddr *ifa;
2435500d3beSWarner Losh 	struct radix_node_head	*rnh;
2445500d3beSWarner Losh 	int s;
2455500d3beSWarner Losh 	int i;
2466182fdbdSPeter Wemm 
2476182fdbdSPeter Wemm 	/*
2486182fdbdSPeter Wemm 	 * Remove routes and flush queues.
2496182fdbdSPeter Wemm 	 */
2505500d3beSWarner Losh 	s = splnet();
2516182fdbdSPeter Wemm 	if_down(ifp);
2526182fdbdSPeter Wemm 
2536182fdbdSPeter Wemm 	/*
254f9132cebSJonathan Lemon 	 * Remove address from ifindex_table[] and maybe decrement if_index.
2556182fdbdSPeter Wemm 	 * Clean up all addresses.
2566182fdbdSPeter Wemm 	 */
257f9132cebSJonathan Lemon 	ifaddr_byindex(ifp->if_index) = NULL;
258f9132cebSJonathan Lemon 
259f9132cebSJonathan Lemon 	while (if_index > 0 && ifaddr_byindex(if_index) == NULL)
2606182fdbdSPeter Wemm 		if_index--;
2616182fdbdSPeter Wemm 
2626182fdbdSPeter Wemm 	for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa;
2636182fdbdSPeter Wemm 	     ifa = TAILQ_FIRST(&ifp->if_addrhead)) {
2640d0f9d1eSYoshinobu Inoue #ifdef INET
265aa6be122SWarner Losh 		/* XXX: Ugly!! ad hoc just for INET */
266aa6be122SWarner Losh 		if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
267aa6be122SWarner Losh 			struct ifaliasreq ifr;
268aa6be122SWarner Losh 
269aa6be122SWarner Losh 			bzero(&ifr, sizeof(ifr));
270aa6be122SWarner Losh 			ifr.ifra_addr = *ifa->ifa_addr;
271aa6be122SWarner Losh 			if (ifa->ifa_dstaddr)
272aa6be122SWarner Losh 				ifr.ifra_broadaddr = *ifa->ifa_dstaddr;
273aa6be122SWarner Losh 			if (in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp,
274aa6be122SWarner Losh 			    NULL) == 0)
275aa6be122SWarner Losh 				continue;
276aa6be122SWarner Losh 		}
2770d0f9d1eSYoshinobu Inoue #endif /* INET */
2780d0f9d1eSYoshinobu Inoue #ifdef INET6
2790d0f9d1eSYoshinobu Inoue 		if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
28033841545SHajimu UMEMOTO 			in6_purgeaddr(ifa);
281978ee2edSJun-ichiro itojun Hagino 			/* ifp_addrhead is already updated */
2820d0f9d1eSYoshinobu Inoue 			continue;
2830d0f9d1eSYoshinobu Inoue 		}
2840d0f9d1eSYoshinobu Inoue #endif /* INET6 */
2856182fdbdSPeter Wemm 		TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
2866182fdbdSPeter Wemm 		IFAFREE(ifa);
2876182fdbdSPeter Wemm 	}
2886182fdbdSPeter Wemm 
28933841545SHajimu UMEMOTO #ifdef INET6
29033841545SHajimu UMEMOTO 	/*
29133841545SHajimu UMEMOTO 	 * Remove all IPv6 kernel structs related to ifp.  This should be done
29233841545SHajimu UMEMOTO 	 * before removing routing entries below, since IPv6 interface direct
29333841545SHajimu UMEMOTO 	 * routes are expected to be removed by the IPv6-specific kernel API.
29433841545SHajimu UMEMOTO 	 * Otherwise, the kernel will detect some inconsistency and bark it.
29533841545SHajimu UMEMOTO 	 */
29633841545SHajimu UMEMOTO 	in6_ifdetach(ifp);
29733841545SHajimu UMEMOTO #endif
29833841545SHajimu UMEMOTO 
2995500d3beSWarner Losh 	/*
3005500d3beSWarner Losh 	 * Delete all remaining routes using this interface
3015500d3beSWarner Losh 	 * Unfortuneatly the only way to do this is to slog through
3025500d3beSWarner Losh 	 * the entire routing table looking for routes which point
3035500d3beSWarner Losh 	 * to this interface...oh well...
3045500d3beSWarner Losh 	 */
3055500d3beSWarner Losh 	for (i = 1; i <= AF_MAX; i++) {
3065500d3beSWarner Losh 		if ((rnh = rt_tables[i]) == NULL)
3075500d3beSWarner Losh 			continue;
3085500d3beSWarner Losh 		(void) rnh->rnh_walktree(rnh, if_rtdel, ifp);
3095500d3beSWarner Losh 	}
3105500d3beSWarner Losh 
3116182fdbdSPeter Wemm 	TAILQ_REMOVE(&ifnet, ifp, if_link);
312df5e1987SJonathan Lemon 	mtx_destroy(&ifp->if_snd.ifq_mtx);
3135500d3beSWarner Losh 	splx(s);
3145500d3beSWarner Losh }
3155500d3beSWarner Losh 
3165500d3beSWarner Losh /*
3175500d3beSWarner Losh  * Delete Routes for a Network Interface
3185500d3beSWarner Losh  *
3195500d3beSWarner Losh  * Called for each routing entry via the rnh->rnh_walktree() call above
3205500d3beSWarner Losh  * to delete all route entries referencing a detaching network interface.
3215500d3beSWarner Losh  *
3225500d3beSWarner Losh  * Arguments:
3235500d3beSWarner Losh  *	rn	pointer to node in the routing table
3245500d3beSWarner Losh  *	arg	argument passed to rnh->rnh_walktree() - detaching interface
3255500d3beSWarner Losh  *
3265500d3beSWarner Losh  * Returns:
3275500d3beSWarner Losh  *	0	successful
3285500d3beSWarner Losh  *	errno	failed - reason indicated
3295500d3beSWarner Losh  *
3305500d3beSWarner Losh  */
3315500d3beSWarner Losh static int
3325500d3beSWarner Losh if_rtdel(rn, arg)
3335500d3beSWarner Losh 	struct radix_node	*rn;
3345500d3beSWarner Losh 	void			*arg;
3355500d3beSWarner Losh {
3365500d3beSWarner Losh 	struct rtentry	*rt = (struct rtentry *)rn;
3375500d3beSWarner Losh 	struct ifnet	*ifp = arg;
3385500d3beSWarner Losh 	int		err;
3395500d3beSWarner Losh 
3405500d3beSWarner Losh 	if (rt->rt_ifp == ifp) {
3415500d3beSWarner Losh 
3425500d3beSWarner Losh 		/*
3435500d3beSWarner Losh 		 * Protect (sorta) against walktree recursion problems
3445500d3beSWarner Losh 		 * with cloned routes
3455500d3beSWarner Losh 		 */
3465500d3beSWarner Losh 		if ((rt->rt_flags & RTF_UP) == 0)
3475500d3beSWarner Losh 			return (0);
3485500d3beSWarner Losh 
3495500d3beSWarner Losh 		err = rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
3505500d3beSWarner Losh 				rt_mask(rt), rt->rt_flags,
3515500d3beSWarner Losh 				(struct rtentry **) NULL);
3525500d3beSWarner Losh 		if (err) {
3535500d3beSWarner Losh 			log(LOG_WARNING, "if_rtdel: error %d\n", err);
3545500d3beSWarner Losh 		}
3555500d3beSWarner Losh 	}
3565500d3beSWarner Losh 
3575500d3beSWarner Losh 	return (0);
3586182fdbdSPeter Wemm }
3596182fdbdSPeter Wemm 
360df8bae1dSRodney W. Grimes /*
36130aad87dSBrooks Davis  * Create a clone network interface.
36230aad87dSBrooks Davis  */
36330aad87dSBrooks Davis int
36430aad87dSBrooks Davis if_clone_create(name, len)
36530aad87dSBrooks Davis 	char *name;
36630aad87dSBrooks Davis 	int len;
36730aad87dSBrooks Davis {
36830aad87dSBrooks Davis 	struct if_clone *ifc;
36930aad87dSBrooks Davis 	char *dp;
37030aad87dSBrooks Davis 	int wildcard;
37130aad87dSBrooks Davis 	int unit;
37230aad87dSBrooks Davis 	int err;
37330aad87dSBrooks Davis 
37430aad87dSBrooks Davis 	ifc = if_clone_lookup(name, &unit);
37530aad87dSBrooks Davis 	if (ifc == NULL)
37630aad87dSBrooks Davis 		return (EINVAL);
37730aad87dSBrooks Davis 
37830aad87dSBrooks Davis 	if (ifunit(name) != NULL)
37930aad87dSBrooks Davis 		return (EEXIST);
38030aad87dSBrooks Davis 
38130aad87dSBrooks Davis 	wildcard = (unit < 0);
38230aad87dSBrooks Davis 
38330aad87dSBrooks Davis 	err = (*ifc->ifc_create)(ifc, &unit);
38430aad87dSBrooks Davis 	if (err != 0)
38530aad87dSBrooks Davis 		return (err);
38630aad87dSBrooks Davis 
38730aad87dSBrooks Davis 	/* In the wildcard case, we need to update the name. */
38830aad87dSBrooks Davis 	if (wildcard) {
38930aad87dSBrooks Davis 		for (dp = name; *dp != '\0'; dp++);
39030aad87dSBrooks Davis 		if (snprintf(dp, len - (dp-name), "%d", unit) >
39130aad87dSBrooks Davis 		    len - (dp-name) - 1) {
39230aad87dSBrooks Davis 			/*
39330aad87dSBrooks Davis 			 * This can only be a programmer error and
39430aad87dSBrooks Davis 			 * there's no straightforward way to recover if
39530aad87dSBrooks Davis 			 * it happens.
39630aad87dSBrooks Davis 			 */
39730aad87dSBrooks Davis 			panic("if_clone_create(): interface name too long");
39830aad87dSBrooks Davis 		}
39930aad87dSBrooks Davis 
40030aad87dSBrooks Davis 	}
40130aad87dSBrooks Davis 
40230aad87dSBrooks Davis 	return (0);
40330aad87dSBrooks Davis }
40430aad87dSBrooks Davis 
40530aad87dSBrooks Davis /*
40630aad87dSBrooks Davis  * Destroy a clone network interface.
40730aad87dSBrooks Davis  */
40830aad87dSBrooks Davis int
40930aad87dSBrooks Davis if_clone_destroy(name)
41030aad87dSBrooks Davis 	const char *name;
41130aad87dSBrooks Davis {
41230aad87dSBrooks Davis 	struct if_clone *ifc;
41330aad87dSBrooks Davis 	struct ifnet *ifp;
41430aad87dSBrooks Davis 
41530aad87dSBrooks Davis 	ifc = if_clone_lookup(name, NULL);
41630aad87dSBrooks Davis 	if (ifc == NULL)
41730aad87dSBrooks Davis 		return (EINVAL);
41830aad87dSBrooks Davis 
41930aad87dSBrooks Davis 	ifp = ifunit(name);
42030aad87dSBrooks Davis 	if (ifp == NULL)
42130aad87dSBrooks Davis 		return (ENXIO);
42230aad87dSBrooks Davis 
42330aad87dSBrooks Davis 	if (ifc->ifc_destroy == NULL)
42430aad87dSBrooks Davis 		return (EOPNOTSUPP);
42530aad87dSBrooks Davis 
42630aad87dSBrooks Davis 	(*ifc->ifc_destroy)(ifp);
42730aad87dSBrooks Davis 	return (0);
42830aad87dSBrooks Davis }
42930aad87dSBrooks Davis 
43030aad87dSBrooks Davis /*
43130aad87dSBrooks Davis  * Look up a network interface cloner.
43230aad87dSBrooks Davis  */
4330b59d917SJonathan Lemon static struct if_clone *
43430aad87dSBrooks Davis if_clone_lookup(name, unitp)
43530aad87dSBrooks Davis 	const char *name;
43630aad87dSBrooks Davis 	int *unitp;
43730aad87dSBrooks Davis {
43830aad87dSBrooks Davis 	struct if_clone *ifc;
43930aad87dSBrooks Davis 	const char *cp;
44030aad87dSBrooks Davis 	int i;
44130aad87dSBrooks Davis 
44230aad87dSBrooks Davis 	for (ifc = LIST_FIRST(&if_cloners); ifc != NULL;) {
44330aad87dSBrooks Davis 		for (cp = name, i = 0; i < ifc->ifc_namelen; i++, cp++) {
44430aad87dSBrooks Davis 			if (ifc->ifc_name[i] != *cp)
44530aad87dSBrooks Davis 				goto next_ifc;
44630aad87dSBrooks Davis 		}
44730aad87dSBrooks Davis 		goto found_name;
44830aad87dSBrooks Davis  next_ifc:
44930aad87dSBrooks Davis 		ifc = LIST_NEXT(ifc, ifc_list);
45030aad87dSBrooks Davis 	}
45130aad87dSBrooks Davis 
45230aad87dSBrooks Davis 	/* No match. */
45330aad87dSBrooks Davis 	return ((struct if_clone *)NULL);
45430aad87dSBrooks Davis 
45530aad87dSBrooks Davis  found_name:
45630aad87dSBrooks Davis 	if (*cp == '\0') {
45730aad87dSBrooks Davis 		i = -1;
45830aad87dSBrooks Davis 	} else {
45930aad87dSBrooks Davis 		for (i = 0; *cp != '\0'; cp++) {
46030aad87dSBrooks Davis 			if (*cp < '0' || *cp > '9') {
46130aad87dSBrooks Davis 				/* Bogus unit number. */
46230aad87dSBrooks Davis 				return (NULL);
46330aad87dSBrooks Davis 			}
46430aad87dSBrooks Davis 			i = (i * 10) + (*cp - '0');
46530aad87dSBrooks Davis 		}
46630aad87dSBrooks Davis 	}
46730aad87dSBrooks Davis 
46830aad87dSBrooks Davis 	if (unitp != NULL)
46930aad87dSBrooks Davis 		*unitp = i;
47030aad87dSBrooks Davis 	return (ifc);
47130aad87dSBrooks Davis }
47230aad87dSBrooks Davis 
47330aad87dSBrooks Davis /*
47430aad87dSBrooks Davis  * Register a network interface cloner.
47530aad87dSBrooks Davis  */
47630aad87dSBrooks Davis void
47730aad87dSBrooks Davis if_clone_attach(ifc)
47830aad87dSBrooks Davis 	struct if_clone *ifc;
47930aad87dSBrooks Davis {
48030aad87dSBrooks Davis 
48130aad87dSBrooks Davis 	LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
48230aad87dSBrooks Davis 	if_cloners_count++;
48330aad87dSBrooks Davis }
48430aad87dSBrooks Davis 
48530aad87dSBrooks Davis /*
48630aad87dSBrooks Davis  * Unregister a network interface cloner.
48730aad87dSBrooks Davis  */
48830aad87dSBrooks Davis void
48930aad87dSBrooks Davis if_clone_detach(ifc)
49030aad87dSBrooks Davis 	struct if_clone *ifc;
49130aad87dSBrooks Davis {
49230aad87dSBrooks Davis 
49330aad87dSBrooks Davis 	LIST_REMOVE(ifc, ifc_list);
49430aad87dSBrooks Davis 	if_cloners_count--;
49530aad87dSBrooks Davis }
49630aad87dSBrooks Davis 
49730aad87dSBrooks Davis /*
49830aad87dSBrooks Davis  * Provide list of interface cloners to userspace.
49930aad87dSBrooks Davis  */
5000b59d917SJonathan Lemon static int
50130aad87dSBrooks Davis if_clone_list(ifcr)
50230aad87dSBrooks Davis 	struct if_clonereq *ifcr;
50330aad87dSBrooks Davis {
50430aad87dSBrooks Davis 	char outbuf[IFNAMSIZ], *dst;
50530aad87dSBrooks Davis 	struct if_clone *ifc;
50630aad87dSBrooks Davis 	int count, error = 0;
50730aad87dSBrooks Davis 
50830aad87dSBrooks Davis 	ifcr->ifcr_total = if_cloners_count;
50930aad87dSBrooks Davis 	if ((dst = ifcr->ifcr_buffer) == NULL) {
51030aad87dSBrooks Davis 		/* Just asking how many there are. */
51130aad87dSBrooks Davis 		return (0);
51230aad87dSBrooks Davis 	}
51330aad87dSBrooks Davis 
51430aad87dSBrooks Davis 	if (ifcr->ifcr_count < 0)
51530aad87dSBrooks Davis 		return (EINVAL);
51630aad87dSBrooks Davis 
51730aad87dSBrooks Davis 	count = (if_cloners_count < ifcr->ifcr_count) ?
51830aad87dSBrooks Davis 	    if_cloners_count : ifcr->ifcr_count;
51930aad87dSBrooks Davis 
52030aad87dSBrooks Davis 	for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
52130aad87dSBrooks Davis 	     ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
52230aad87dSBrooks Davis 		strncpy(outbuf, ifc->ifc_name, IFNAMSIZ);
52330aad87dSBrooks Davis 		outbuf[IFNAMSIZ - 1] = '\0';	/* sanity */
52430aad87dSBrooks Davis 		error = copyout(outbuf, dst, IFNAMSIZ);
52530aad87dSBrooks Davis 		if (error)
52630aad87dSBrooks Davis 			break;
52730aad87dSBrooks Davis 	}
52830aad87dSBrooks Davis 
52930aad87dSBrooks Davis 	return (error);
53030aad87dSBrooks Davis }
53130aad87dSBrooks Davis 
53230aad87dSBrooks Davis /*
533df8bae1dSRodney W. Grimes  * Locate an interface based on a complete address.
534df8bae1dSRodney W. Grimes  */
535df8bae1dSRodney W. Grimes /*ARGSUSED*/
536df8bae1dSRodney W. Grimes struct ifaddr *
537df8bae1dSRodney W. Grimes ifa_ifwithaddr(addr)
5380b59d917SJonathan Lemon 	struct sockaddr *addr;
539df8bae1dSRodney W. Grimes {
5400b59d917SJonathan Lemon 	struct ifnet *ifp;
5410b59d917SJonathan Lemon 	struct ifaddr *ifa;
542df8bae1dSRodney W. Grimes 
543df8bae1dSRodney W. Grimes #define	equal(a1, a2) \
544df8bae1dSRodney W. Grimes   (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
545fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link)
54637d40066SPoul-Henning Kamp 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
547df8bae1dSRodney W. Grimes 			if (ifa->ifa_addr->sa_family != addr->sa_family)
548df8bae1dSRodney W. Grimes 				continue;
549df8bae1dSRodney W. Grimes 			if (equal(addr, ifa->ifa_addr))
5500b59d917SJonathan Lemon 				goto done;
55182cd038dSYoshinobu Inoue 			/* IP6 doesn't have broadcast */
5520b59d917SJonathan Lemon 			if ((ifp->if_flags & IFF_BROADCAST) &&
5530b59d917SJonathan Lemon 			    ifa->ifa_broadaddr &&
55482cd038dSYoshinobu Inoue 			    ifa->ifa_broadaddr->sa_len != 0 &&
555df8bae1dSRodney W. Grimes 			    equal(ifa->ifa_broadaddr, addr))
5560b59d917SJonathan Lemon 				goto done;
5570b59d917SJonathan Lemon 		}
5580b59d917SJonathan Lemon 	ifa = NULL;
5590b59d917SJonathan Lemon done:
560df8bae1dSRodney W. Grimes 	return (ifa);
561df8bae1dSRodney W. Grimes }
5620b59d917SJonathan Lemon 
563df8bae1dSRodney W. Grimes /*
564df8bae1dSRodney W. Grimes  * Locate the point to point interface with a given destination address.
565df8bae1dSRodney W. Grimes  */
566df8bae1dSRodney W. Grimes /*ARGSUSED*/
567df8bae1dSRodney W. Grimes struct ifaddr *
568df8bae1dSRodney W. Grimes ifa_ifwithdstaddr(addr)
5690b59d917SJonathan Lemon 	struct sockaddr *addr;
570df8bae1dSRodney W. Grimes {
5710b59d917SJonathan Lemon 	struct ifnet *ifp;
5720b59d917SJonathan Lemon 	struct ifaddr *ifa;
573df8bae1dSRodney W. Grimes 
5740b59d917SJonathan Lemon 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
5750b59d917SJonathan Lemon 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
5760b59d917SJonathan Lemon 			continue;
57737d40066SPoul-Henning Kamp 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
578df8bae1dSRodney W. Grimes 			if (ifa->ifa_addr->sa_family != addr->sa_family)
579df8bae1dSRodney W. Grimes 				continue;
58055088a1cSDavid Greenman 			if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))
5810b59d917SJonathan Lemon 				goto done;
582df8bae1dSRodney W. Grimes 		}
5830b59d917SJonathan Lemon 	}
5840b59d917SJonathan Lemon 	ifa = NULL;
5850b59d917SJonathan Lemon done:
5860b59d917SJonathan Lemon 	return (ifa);
587df8bae1dSRodney W. Grimes }
588df8bae1dSRodney W. Grimes 
589df8bae1dSRodney W. Grimes /*
590df8bae1dSRodney W. Grimes  * Find an interface on a specific network.  If many, choice
591df8bae1dSRodney W. Grimes  * is most specific found.
592df8bae1dSRodney W. Grimes  */
593df8bae1dSRodney W. Grimes struct ifaddr *
594df8bae1dSRodney W. Grimes ifa_ifwithnet(addr)
595df8bae1dSRodney W. Grimes 	struct sockaddr *addr;
596df8bae1dSRodney W. Grimes {
597df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
598df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
599df8bae1dSRodney W. Grimes 	struct ifaddr *ifa_maybe = (struct ifaddr *) 0;
600df8bae1dSRodney W. Grimes 	u_int af = addr->sa_family;
601df8bae1dSRodney W. Grimes 	char *addr_data = addr->sa_data, *cplim;
602df8bae1dSRodney W. Grimes 
6037e2a6151SJulian Elischer 	/*
6047e2a6151SJulian Elischer 	 * AF_LINK addresses can be looked up directly by their index number,
6057e2a6151SJulian Elischer 	 * so do that if we can.
6067e2a6151SJulian Elischer 	 */
607df8bae1dSRodney W. Grimes 	if (af == AF_LINK) {
608df8bae1dSRodney W. Grimes 	    register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
609df8bae1dSRodney W. Grimes 	    if (sdl->sdl_index && sdl->sdl_index <= if_index)
610f9132cebSJonathan Lemon 		return (ifaddr_byindex(sdl->sdl_index));
611df8bae1dSRodney W. Grimes 	}
6127e2a6151SJulian Elischer 
6137e2a6151SJulian Elischer 	/*
6147e2a6151SJulian Elischer 	 * Scan though each interface, looking for ones that have
6157e2a6151SJulian Elischer 	 * addresses in this address family.
6167e2a6151SJulian Elischer 	 */
617fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
61837d40066SPoul-Henning Kamp 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
619df8bae1dSRodney W. Grimes 			register char *cp, *cp2, *cp3;
620df8bae1dSRodney W. Grimes 
621523a02aaSDavid Greenman 			if (ifa->ifa_addr->sa_family != af)
622df8bae1dSRodney W. Grimes next:				continue;
62382cd038dSYoshinobu Inoue 			if (
62482cd038dSYoshinobu Inoue #ifdef INET6 /* XXX: for maching gif tunnel dst as routing entry gateway */
62582cd038dSYoshinobu Inoue 			    addr->sa_family != AF_INET6 &&
62682cd038dSYoshinobu Inoue #endif
62782cd038dSYoshinobu Inoue 			    ifp->if_flags & IFF_POINTOPOINT) {
6287e2a6151SJulian Elischer 				/*
6297e2a6151SJulian Elischer 				 * This is a bit broken as it doesn't
6307e2a6151SJulian Elischer 				 * take into account that the remote end may
6317e2a6151SJulian Elischer 				 * be a single node in the network we are
6327e2a6151SJulian Elischer 				 * looking for.
6337e2a6151SJulian Elischer 				 * The trouble is that we don't know the
6347e2a6151SJulian Elischer 				 * netmask for the remote end.
6357e2a6151SJulian Elischer 				 */
636fcd6781aSGarrett Wollman 				if (ifa->ifa_dstaddr != 0
637fcd6781aSGarrett Wollman 				    && equal(addr, ifa->ifa_dstaddr))
6380b59d917SJonathan Lemon 					goto done;
6393740e2adSDavid Greenman 			} else {
6407e2a6151SJulian Elischer 				/*
6417ed8f465SJulian Elischer 				 * if we have a special address handler,
6427ed8f465SJulian Elischer 				 * then use it instead of the generic one.
6437ed8f465SJulian Elischer 				 */
6447ed8f465SJulian Elischer 	          		if (ifa->ifa_claim_addr) {
6450b59d917SJonathan Lemon 					if ((*ifa->ifa_claim_addr)(ifa, addr))
6460b59d917SJonathan Lemon 						goto done;
6477ed8f465SJulian Elischer 					continue;
6487ed8f465SJulian Elischer 				}
6497ed8f465SJulian Elischer 
6507ed8f465SJulian Elischer 				/*
6517e2a6151SJulian Elischer 				 * Scan all the bits in the ifa's address.
6527e2a6151SJulian Elischer 				 * If a bit dissagrees with what we are
6537e2a6151SJulian Elischer 				 * looking for, mask it with the netmask
6547e2a6151SJulian Elischer 				 * to see if it really matters.
6557e2a6151SJulian Elischer 				 * (A byte at a time)
6567e2a6151SJulian Elischer 				 */
657523a02aaSDavid Greenman 				if (ifa->ifa_netmask == 0)
658523a02aaSDavid Greenman 					continue;
659df8bae1dSRodney W. Grimes 				cp = addr_data;
660df8bae1dSRodney W. Grimes 				cp2 = ifa->ifa_addr->sa_data;
661df8bae1dSRodney W. Grimes 				cp3 = ifa->ifa_netmask->sa_data;
6627e2a6151SJulian Elischer 				cplim = ifa->ifa_netmask->sa_len
6637e2a6151SJulian Elischer 					+ (char *)ifa->ifa_netmask;
664df8bae1dSRodney W. Grimes 				while (cp3 < cplim)
665df8bae1dSRodney W. Grimes 					if ((*cp++ ^ *cp2++) & *cp3++)
6667e2a6151SJulian Elischer 						goto next; /* next address! */
6677e2a6151SJulian Elischer 				/*
6687e2a6151SJulian Elischer 				 * If the netmask of what we just found
6697e2a6151SJulian Elischer 				 * is more specific than what we had before
6707e2a6151SJulian Elischer 				 * (if we had one) then remember the new one
6717e2a6151SJulian Elischer 				 * before continuing to search
6727e2a6151SJulian Elischer 				 * for an even better one.
6737e2a6151SJulian Elischer 				 */
674df8bae1dSRodney W. Grimes 				if (ifa_maybe == 0 ||
675df8bae1dSRodney W. Grimes 				    rn_refines((caddr_t)ifa->ifa_netmask,
676df8bae1dSRodney W. Grimes 				    (caddr_t)ifa_maybe->ifa_netmask))
677df8bae1dSRodney W. Grimes 					ifa_maybe = ifa;
678df8bae1dSRodney W. Grimes 			}
679b2af64fdSDavid Greenman 		}
680b2af64fdSDavid Greenman 	}
6810b59d917SJonathan Lemon 	ifa = ifa_maybe;
6820b59d917SJonathan Lemon done:
6830b59d917SJonathan Lemon 	return (ifa);
684df8bae1dSRodney W. Grimes }
685df8bae1dSRodney W. Grimes 
686df8bae1dSRodney W. Grimes /*
687df8bae1dSRodney W. Grimes  * Find an interface address specific to an interface best matching
688df8bae1dSRodney W. Grimes  * a given address.
689df8bae1dSRodney W. Grimes  */
690df8bae1dSRodney W. Grimes struct ifaddr *
691df8bae1dSRodney W. Grimes ifaof_ifpforaddr(addr, ifp)
692df8bae1dSRodney W. Grimes 	struct sockaddr *addr;
693df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
694df8bae1dSRodney W. Grimes {
695df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
696df8bae1dSRodney W. Grimes 	register char *cp, *cp2, *cp3;
697df8bae1dSRodney W. Grimes 	register char *cplim;
698df8bae1dSRodney W. Grimes 	struct ifaddr *ifa_maybe = 0;
699df8bae1dSRodney W. Grimes 	u_int af = addr->sa_family;
700df8bae1dSRodney W. Grimes 
701df8bae1dSRodney W. Grimes 	if (af >= AF_MAX)
702df8bae1dSRodney W. Grimes 		return (0);
70337d40066SPoul-Henning Kamp 	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
704df8bae1dSRodney W. Grimes 		if (ifa->ifa_addr->sa_family != af)
705df8bae1dSRodney W. Grimes 			continue;
706381dd1d2SJulian Elischer 		if (ifa_maybe == 0)
707df8bae1dSRodney W. Grimes 			ifa_maybe = ifa;
708df8bae1dSRodney W. Grimes 		if (ifa->ifa_netmask == 0) {
709df8bae1dSRodney W. Grimes 			if (equal(addr, ifa->ifa_addr) ||
710df8bae1dSRodney W. Grimes 			    (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
711df8bae1dSRodney W. Grimes 				return (ifa);
712df8bae1dSRodney W. Grimes 			continue;
713df8bae1dSRodney W. Grimes 		}
714b2af64fdSDavid Greenman 		if (ifp->if_flags & IFF_POINTOPOINT) {
715b2af64fdSDavid Greenman 			if (equal(addr, ifa->ifa_dstaddr))
716b2af64fdSDavid Greenman 				return (ifa);
7173740e2adSDavid Greenman 		} else {
718df8bae1dSRodney W. Grimes 			cp = addr->sa_data;
719df8bae1dSRodney W. Grimes 			cp2 = ifa->ifa_addr->sa_data;
720df8bae1dSRodney W. Grimes 			cp3 = ifa->ifa_netmask->sa_data;
721df8bae1dSRodney W. Grimes 			cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
722df8bae1dSRodney W. Grimes 			for (; cp3 < cplim; cp3++)
723df8bae1dSRodney W. Grimes 				if ((*cp++ ^ *cp2++) & *cp3)
724df8bae1dSRodney W. Grimes 					break;
725df8bae1dSRodney W. Grimes 			if (cp3 == cplim)
726df8bae1dSRodney W. Grimes 				return (ifa);
727df8bae1dSRodney W. Grimes 		}
728b2af64fdSDavid Greenman 	}
729f9132cebSJonathan Lemon 	ifa = ifa_maybe;
730f9132cebSJonathan Lemon done:
731f9132cebSJonathan Lemon 	return (ifa);
732df8bae1dSRodney W. Grimes }
733df8bae1dSRodney W. Grimes 
734df8bae1dSRodney W. Grimes #include <net/route.h>
735df8bae1dSRodney W. Grimes 
736df8bae1dSRodney W. Grimes /*
737df8bae1dSRodney W. Grimes  * Default action when installing a route with a Link Level gateway.
738df8bae1dSRodney W. Grimes  * Lookup an appropriate real ifa to point to.
739df8bae1dSRodney W. Grimes  * This should be moved to /sys/net/link.c eventually.
740df8bae1dSRodney W. Grimes  */
7413bda9f9bSPoul-Henning Kamp static void
742df8bae1dSRodney W. Grimes link_rtrequest(cmd, rt, sa)
743df8bae1dSRodney W. Grimes 	int cmd;
744df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
745df8bae1dSRodney W. Grimes 	struct sockaddr *sa;
746df8bae1dSRodney W. Grimes {
747df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
748df8bae1dSRodney W. Grimes 	struct sockaddr *dst;
749df8bae1dSRodney W. Grimes 	struct ifnet *ifp;
750df8bae1dSRodney W. Grimes 
751df8bae1dSRodney W. Grimes 	if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) ||
752df8bae1dSRodney W. Grimes 	    ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0))
753df8bae1dSRodney W. Grimes 		return;
7549448326fSPoul-Henning Kamp 	ifa = ifaof_ifpforaddr(dst, ifp);
7559448326fSPoul-Henning Kamp 	if (ifa) {
756df8bae1dSRodney W. Grimes 		IFAFREE(rt->rt_ifa);
757df8bae1dSRodney W. Grimes 		rt->rt_ifa = ifa;
758df8bae1dSRodney W. Grimes 		ifa->ifa_refcnt++;
759df8bae1dSRodney W. Grimes 		if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest)
760df8bae1dSRodney W. Grimes 			ifa->ifa_rtrequest(cmd, rt, sa);
761df8bae1dSRodney W. Grimes 	}
762df8bae1dSRodney W. Grimes }
763df8bae1dSRodney W. Grimes 
764df8bae1dSRodney W. Grimes /*
765df8bae1dSRodney W. Grimes  * Mark an interface down and notify protocols of
766df8bae1dSRodney W. Grimes  * the transition.
767df8bae1dSRodney W. Grimes  * NOTE: must be called at splnet or eqivalent.
768df8bae1dSRodney W. Grimes  */
769df8bae1dSRodney W. Grimes void
770e8c2601dSPoul-Henning Kamp if_unroute(ifp, flag, fam)
771df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
772e8c2601dSPoul-Henning Kamp 	int flag, fam;
773df8bae1dSRodney W. Grimes {
774df8bae1dSRodney W. Grimes 	register struct ifaddr *ifa;
775df8bae1dSRodney W. Grimes 
776e8c2601dSPoul-Henning Kamp 	ifp->if_flags &= ~flag;
77798b9590eSPoul-Henning Kamp 	getmicrotime(&ifp->if_lastchange);
778e8c2601dSPoul-Henning Kamp 	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
779e8c2601dSPoul-Henning Kamp 		if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family))
780df8bae1dSRodney W. Grimes 			pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
781df8bae1dSRodney W. Grimes 	if_qflush(&ifp->if_snd);
782df8bae1dSRodney W. Grimes 	rt_ifmsg(ifp);
783df8bae1dSRodney W. Grimes }
784df8bae1dSRodney W. Grimes 
785df8bae1dSRodney W. Grimes /*
786df8bae1dSRodney W. Grimes  * Mark an interface up and notify protocols of
787df8bae1dSRodney W. Grimes  * the transition.
788df8bae1dSRodney W. Grimes  * NOTE: must be called at splnet or eqivalent.
789df8bae1dSRodney W. Grimes  */
790df8bae1dSRodney W. Grimes void
791e8c2601dSPoul-Henning Kamp if_route(ifp, flag, fam)
792df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
793e8c2601dSPoul-Henning Kamp 	int flag, fam;
794df8bae1dSRodney W. Grimes {
795176395b2SGarrett Wollman 	register struct ifaddr *ifa;
796df8bae1dSRodney W. Grimes 
797e8c2601dSPoul-Henning Kamp 	ifp->if_flags |= flag;
79898b9590eSPoul-Henning Kamp 	getmicrotime(&ifp->if_lastchange);
799e8c2601dSPoul-Henning Kamp 	TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
800e8c2601dSPoul-Henning Kamp 		if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family))
801df8bae1dSRodney W. Grimes 			pfctlinput(PRC_IFUP, ifa->ifa_addr);
802df8bae1dSRodney W. Grimes 	rt_ifmsg(ifp);
80382cd038dSYoshinobu Inoue #ifdef INET6
80482cd038dSYoshinobu Inoue 	in6_if_up(ifp);
80582cd038dSYoshinobu Inoue #endif
806df8bae1dSRodney W. Grimes }
807df8bae1dSRodney W. Grimes 
808df8bae1dSRodney W. Grimes /*
809e8c2601dSPoul-Henning Kamp  * Mark an interface down and notify protocols of
810e8c2601dSPoul-Henning Kamp  * the transition.
811e8c2601dSPoul-Henning Kamp  * NOTE: must be called at splnet or eqivalent.
812e8c2601dSPoul-Henning Kamp  */
813e8c2601dSPoul-Henning Kamp void
814e8c2601dSPoul-Henning Kamp if_down(ifp)
815e8c2601dSPoul-Henning Kamp 	register struct ifnet *ifp;
816e8c2601dSPoul-Henning Kamp {
817e8c2601dSPoul-Henning Kamp 
818e8c2601dSPoul-Henning Kamp 	if_unroute(ifp, IFF_UP, AF_UNSPEC);
819e8c2601dSPoul-Henning Kamp }
820e8c2601dSPoul-Henning Kamp 
821e8c2601dSPoul-Henning Kamp /*
822e8c2601dSPoul-Henning Kamp  * Mark an interface up and notify protocols of
823e8c2601dSPoul-Henning Kamp  * the transition.
824e8c2601dSPoul-Henning Kamp  * NOTE: must be called at splnet or eqivalent.
825e8c2601dSPoul-Henning Kamp  */
826e8c2601dSPoul-Henning Kamp void
827e8c2601dSPoul-Henning Kamp if_up(ifp)
828e8c2601dSPoul-Henning Kamp 	register struct ifnet *ifp;
829e8c2601dSPoul-Henning Kamp {
830e8c2601dSPoul-Henning Kamp 
831e8c2601dSPoul-Henning Kamp 	if_route(ifp, IFF_UP, AF_UNSPEC);
832e8c2601dSPoul-Henning Kamp }
833e8c2601dSPoul-Henning Kamp 
834e8c2601dSPoul-Henning Kamp /*
835df8bae1dSRodney W. Grimes  * Flush an interface queue.
836df8bae1dSRodney W. Grimes  */
8373bda9f9bSPoul-Henning Kamp static void
838df8bae1dSRodney W. Grimes if_qflush(ifq)
839df8bae1dSRodney W. Grimes 	register struct ifqueue *ifq;
840df8bae1dSRodney W. Grimes {
841df8bae1dSRodney W. Grimes 	register struct mbuf *m, *n;
842df8bae1dSRodney W. Grimes 
843df8bae1dSRodney W. Grimes 	n = ifq->ifq_head;
8449448326fSPoul-Henning Kamp 	while ((m = n) != 0) {
845df8bae1dSRodney W. Grimes 		n = m->m_act;
846df8bae1dSRodney W. Grimes 		m_freem(m);
847df8bae1dSRodney W. Grimes 	}
848df8bae1dSRodney W. Grimes 	ifq->ifq_head = 0;
849df8bae1dSRodney W. Grimes 	ifq->ifq_tail = 0;
850df8bae1dSRodney W. Grimes 	ifq->ifq_len = 0;
851df8bae1dSRodney W. Grimes }
852df8bae1dSRodney W. Grimes 
853df8bae1dSRodney W. Grimes /*
854df8bae1dSRodney W. Grimes  * Handle interface watchdog timer routines.  Called
855df8bae1dSRodney W. Grimes  * from softclock, we decrement timers (if set) and
856df8bae1dSRodney W. Grimes  * call the appropriate interface routine on expiration.
857df8bae1dSRodney W. Grimes  */
8583bda9f9bSPoul-Henning Kamp static void
859df8bae1dSRodney W. Grimes if_slowtimo(arg)
860df8bae1dSRodney W. Grimes 	void *arg;
861df8bae1dSRodney W. Grimes {
862df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
863df8bae1dSRodney W. Grimes 	int s = splimp();
864df8bae1dSRodney W. Grimes 
865fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
866df8bae1dSRodney W. Grimes 		if (ifp->if_timer == 0 || --ifp->if_timer)
867df8bae1dSRodney W. Grimes 			continue;
868df8bae1dSRodney W. Grimes 		if (ifp->if_watchdog)
8694a5f1499SDavid Greenman 			(*ifp->if_watchdog)(ifp);
870df8bae1dSRodney W. Grimes 	}
871df8bae1dSRodney W. Grimes 	splx(s);
872df8bae1dSRodney W. Grimes 	timeout(if_slowtimo, (void *)0, hz / IFNET_SLOWHZ);
873df8bae1dSRodney W. Grimes }
874df8bae1dSRodney W. Grimes 
875df8bae1dSRodney W. Grimes /*
876df8bae1dSRodney W. Grimes  * Map interface name to
877df8bae1dSRodney W. Grimes  * interface structure pointer.
878df8bae1dSRodney W. Grimes  */
879df8bae1dSRodney W. Grimes struct ifnet *
88030aad87dSBrooks Davis ifunit(const char *name)
881df8bae1dSRodney W. Grimes {
88201f0fef3SJulian Elischer 	char namebuf[IFNAMSIZ + 1];
88330aad87dSBrooks Davis 	const char *cp;
8848b7805e4SBoris Popov 	struct ifnet *ifp;
885df8bae1dSRodney W. Grimes 	int unit;
8868b7805e4SBoris Popov 	unsigned len, m;
8878b7805e4SBoris Popov 	char c;
888df8bae1dSRodney W. Grimes 
8898b7805e4SBoris Popov 	len = strlen(name);
8908b7805e4SBoris Popov 	if (len < 2 || len > IFNAMSIZ)
8918b7805e4SBoris Popov 		return NULL;
8928b7805e4SBoris Popov 	cp = name + len - 1;
8938b7805e4SBoris Popov 	c = *cp;
8948b7805e4SBoris Popov 	if (c < '0' || c > '9')
8958b7805e4SBoris Popov 		return NULL;		/* trailing garbage */
8968b7805e4SBoris Popov 	unit = 0;
8978b7805e4SBoris Popov 	m = 1;
8988b7805e4SBoris Popov 	do {
8998b7805e4SBoris Popov 		if (cp == name)
9008b7805e4SBoris Popov 			return NULL;	/* no interface name */
9018b7805e4SBoris Popov 		unit += (c - '0') * m;
9028b7805e4SBoris Popov 		if (unit > 1000000)
9038b7805e4SBoris Popov 			return NULL;	/* number is unreasonable */
9048b7805e4SBoris Popov 		m *= 10;
9058b7805e4SBoris Popov 		c = *--cp;
9068b7805e4SBoris Popov 	} while (c >= '0' && c <= '9');
907df8bae1dSRodney W. Grimes 	len = cp - name + 1;
9088b7805e4SBoris Popov 	bcopy(name, namebuf, len);
9098b7805e4SBoris Popov 	namebuf[len] = '\0';
91001f0fef3SJulian Elischer 	/*
91101f0fef3SJulian Elischer 	 * Now search all the interfaces for this name/number
91201f0fef3SJulian Elischer 	 */
913fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
9148b7805e4SBoris Popov 		if (strcmp(ifp->if_name, namebuf))
915df8bae1dSRodney W. Grimes 			continue;
916df8bae1dSRodney W. Grimes 		if (unit == ifp->if_unit)
917df8bae1dSRodney W. Grimes 			break;
918df8bae1dSRodney W. Grimes 	}
919df8bae1dSRodney W. Grimes 	return (ifp);
920df8bae1dSRodney W. Grimes }
921df8bae1dSRodney W. Grimes 
92282cd038dSYoshinobu Inoue 
92382cd038dSYoshinobu Inoue /*
92482cd038dSYoshinobu Inoue  * Map interface name in a sockaddr_dl to
92582cd038dSYoshinobu Inoue  * interface structure pointer.
92682cd038dSYoshinobu Inoue  */
92782cd038dSYoshinobu Inoue struct ifnet *
92882cd038dSYoshinobu Inoue if_withname(sa)
92982cd038dSYoshinobu Inoue 	struct sockaddr *sa;
93082cd038dSYoshinobu Inoue {
93182cd038dSYoshinobu Inoue 	char ifname[IFNAMSIZ+1];
93282cd038dSYoshinobu Inoue 	struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
93382cd038dSYoshinobu Inoue 
93482cd038dSYoshinobu Inoue 	if ( (sa->sa_family != AF_LINK) || (sdl->sdl_nlen == 0) ||
93582cd038dSYoshinobu Inoue 	     (sdl->sdl_nlen > IFNAMSIZ) )
93682cd038dSYoshinobu Inoue 		return NULL;
93782cd038dSYoshinobu Inoue 
93882cd038dSYoshinobu Inoue 	/*
93982cd038dSYoshinobu Inoue 	 * ifunit wants a null-terminated name.  It may not be null-terminated
94082cd038dSYoshinobu Inoue 	 * in the sockaddr.  We don't want to change the caller's sockaddr,
94182cd038dSYoshinobu Inoue 	 * and there might not be room to put the trailing null anyway, so we
94282cd038dSYoshinobu Inoue 	 * make a local copy that we know we can null terminate safely.
94382cd038dSYoshinobu Inoue 	 */
94482cd038dSYoshinobu Inoue 
94582cd038dSYoshinobu Inoue 	bcopy(sdl->sdl_data, ifname, sdl->sdl_nlen);
94682cd038dSYoshinobu Inoue 	ifname[sdl->sdl_nlen] = '\0';
94782cd038dSYoshinobu Inoue 	return ifunit(ifname);
94882cd038dSYoshinobu Inoue }
94982cd038dSYoshinobu Inoue 
95082cd038dSYoshinobu Inoue 
951df8bae1dSRodney W. Grimes /*
952df8bae1dSRodney W. Grimes  * Interface ioctls.
953df8bae1dSRodney W. Grimes  */
954df8bae1dSRodney W. Grimes int
955df8bae1dSRodney W. Grimes ifioctl(so, cmd, data, p)
956df8bae1dSRodney W. Grimes 	struct socket *so;
957ecbb00a2SDoug Rabson 	u_long cmd;
958df8bae1dSRodney W. Grimes 	caddr_t data;
959df8bae1dSRodney W. Grimes 	struct proc *p;
960df8bae1dSRodney W. Grimes {
961df8bae1dSRodney W. Grimes 	register struct ifnet *ifp;
962df8bae1dSRodney W. Grimes 	register struct ifreq *ifr;
963413dd0baSPoul-Henning Kamp 	struct ifstat *ifs;
964df8bae1dSRodney W. Grimes 	int error;
96582cd038dSYoshinobu Inoue 	short oif_flags;
966df8bae1dSRodney W. Grimes 
967df8bae1dSRodney W. Grimes 	switch (cmd) {
968df8bae1dSRodney W. Grimes 
969df8bae1dSRodney W. Grimes 	case SIOCGIFCONF:
970df8bae1dSRodney W. Grimes 	case OSIOCGIFCONF:
971df8bae1dSRodney W. Grimes 		return (ifconf(cmd, data));
972df8bae1dSRodney W. Grimes 	}
973df8bae1dSRodney W. Grimes 	ifr = (struct ifreq *)data;
97430aad87dSBrooks Davis 
97530aad87dSBrooks Davis 	switch (cmd) {
97630aad87dSBrooks Davis 	case SIOCIFCREATE:
97730aad87dSBrooks Davis 	case SIOCIFDESTROY:
97830aad87dSBrooks Davis 		if ((error = suser(p)) != 0)
97930aad87dSBrooks Davis 			return (error);
98030aad87dSBrooks Davis 		return ((cmd == SIOCIFCREATE) ?
98130aad87dSBrooks Davis 			if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name)) :
98230aad87dSBrooks Davis 			if_clone_destroy(ifr->ifr_name));
98330aad87dSBrooks Davis 
98430aad87dSBrooks Davis 	case SIOCIFGCLONERS:
98530aad87dSBrooks Davis 		return (if_clone_list((struct if_clonereq *)data));
98630aad87dSBrooks Davis 	}
98730aad87dSBrooks Davis 
988df8bae1dSRodney W. Grimes 	ifp = ifunit(ifr->ifr_name);
989df8bae1dSRodney W. Grimes 	if (ifp == 0)
990df8bae1dSRodney W. Grimes 		return (ENXIO);
991df8bae1dSRodney W. Grimes 	switch (cmd) {
992df8bae1dSRodney W. Grimes 
993df8bae1dSRodney W. Grimes 	case SIOCGIFFLAGS:
994df8bae1dSRodney W. Grimes 		ifr->ifr_flags = ifp->if_flags;
995df8bae1dSRodney W. Grimes 		break;
996df8bae1dSRodney W. Grimes 
997df8bae1dSRodney W. Grimes 	case SIOCGIFMETRIC:
998df8bae1dSRodney W. Grimes 		ifr->ifr_metric = ifp->if_metric;
999df8bae1dSRodney W. Grimes 		break;
1000df8bae1dSRodney W. Grimes 
1001a7028af7SDavid Greenman 	case SIOCGIFMTU:
1002a7028af7SDavid Greenman 		ifr->ifr_mtu = ifp->if_mtu;
1003a7028af7SDavid Greenman 		break;
1004a7028af7SDavid Greenman 
1005074c4a4eSGarrett Wollman 	case SIOCGIFPHYS:
1006074c4a4eSGarrett Wollman 		ifr->ifr_phys = ifp->if_physical;
1007074c4a4eSGarrett Wollman 		break;
1008074c4a4eSGarrett Wollman 
1009df8bae1dSRodney W. Grimes 	case SIOCSIFFLAGS:
1010f711d546SPoul-Henning Kamp 		error = suser(p);
10119448326fSPoul-Henning Kamp 		if (error)
1012df8bae1dSRodney W. Grimes 			return (error);
10134add131eSPoul-Henning Kamp 		ifr->ifr_prevflags = ifp->if_flags;
1014cf4b9371SPoul-Henning Kamp 		if (ifp->if_flags & IFF_SMART) {
1015cf4b9371SPoul-Henning Kamp 			/* Smart drivers twiddle their own routes */
10162f55ead7SPoul-Henning Kamp 		} else if (ifp->if_flags & IFF_UP &&
1017cf4b9371SPoul-Henning Kamp 		    (ifr->ifr_flags & IFF_UP) == 0) {
1018df8bae1dSRodney W. Grimes 			int s = splimp();
1019df8bae1dSRodney W. Grimes 			if_down(ifp);
1020df8bae1dSRodney W. Grimes 			splx(s);
1021cf4b9371SPoul-Henning Kamp 		} else if (ifr->ifr_flags & IFF_UP &&
1022cf4b9371SPoul-Henning Kamp 		    (ifp->if_flags & IFF_UP) == 0) {
1023df8bae1dSRodney W. Grimes 			int s = splimp();
1024df8bae1dSRodney W. Grimes 			if_up(ifp);
1025df8bae1dSRodney W. Grimes 			splx(s);
1026df8bae1dSRodney W. Grimes 		}
1027df8bae1dSRodney W. Grimes 		ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
1028df8bae1dSRodney W. Grimes 			(ifr->ifr_flags &~ IFF_CANTCHANGE);
1029df8bae1dSRodney W. Grimes 		if (ifp->if_ioctl)
1030df8bae1dSRodney W. Grimes 			(void) (*ifp->if_ioctl)(ifp, cmd, data);
103198b9590eSPoul-Henning Kamp 		getmicrotime(&ifp->if_lastchange);
1032df8bae1dSRodney W. Grimes 		break;
1033df8bae1dSRodney W. Grimes 
1034df8bae1dSRodney W. Grimes 	case SIOCSIFMETRIC:
1035f711d546SPoul-Henning Kamp 		error = suser(p);
10369448326fSPoul-Henning Kamp 		if (error)
1037df8bae1dSRodney W. Grimes 			return (error);
1038df8bae1dSRodney W. Grimes 		ifp->if_metric = ifr->ifr_metric;
103998b9590eSPoul-Henning Kamp 		getmicrotime(&ifp->if_lastchange);
1040df8bae1dSRodney W. Grimes 		break;
1041df8bae1dSRodney W. Grimes 
1042074c4a4eSGarrett Wollman 	case SIOCSIFPHYS:
1043f711d546SPoul-Henning Kamp 		error = suser(p);
1044e39a0280SGary Palmer 		if (error)
1045e39a0280SGary Palmer 			return error;
1046e39a0280SGary Palmer 		if (!ifp->if_ioctl)
1047e39a0280SGary Palmer 		        return EOPNOTSUPP;
1048e39a0280SGary Palmer 		error = (*ifp->if_ioctl)(ifp, cmd, data);
1049e39a0280SGary Palmer 		if (error == 0)
105098b9590eSPoul-Henning Kamp 			getmicrotime(&ifp->if_lastchange);
1051e39a0280SGary Palmer 		return(error);
1052074c4a4eSGarrett Wollman 
1053a7028af7SDavid Greenman 	case SIOCSIFMTU:
105482cd038dSYoshinobu Inoue 	{
105582cd038dSYoshinobu Inoue 		u_long oldmtu = ifp->if_mtu;
105682cd038dSYoshinobu Inoue 
1057f711d546SPoul-Henning Kamp 		error = suser(p);
10589448326fSPoul-Henning Kamp 		if (error)
1059a7028af7SDavid Greenman 			return (error);
1060a7028af7SDavid Greenman 		if (ifp->if_ioctl == NULL)
1061a7028af7SDavid Greenman 			return (EOPNOTSUPP);
1062aab3beeeSBrian Somers 		if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU)
106375ee03cbSDavid Greenman 			return (EINVAL);
1064e39a0280SGary Palmer 		error = (*ifp->if_ioctl)(ifp, cmd, data);
106548f71763SRuslan Ermilov 		if (error == 0) {
106698b9590eSPoul-Henning Kamp 			getmicrotime(&ifp->if_lastchange);
106748f71763SRuslan Ermilov 			rt_ifmsg(ifp);
106848f71763SRuslan Ermilov 		}
106982cd038dSYoshinobu Inoue 		/*
107082cd038dSYoshinobu Inoue 		 * If the link MTU changed, do network layer specific procedure.
107182cd038dSYoshinobu Inoue 		 */
107282cd038dSYoshinobu Inoue 		if (ifp->if_mtu != oldmtu) {
107382cd038dSYoshinobu Inoue #ifdef INET6
107482cd038dSYoshinobu Inoue 			nd6_setmtu(ifp);
107582cd038dSYoshinobu Inoue #endif
107682cd038dSYoshinobu Inoue 		}
1077e39a0280SGary Palmer 		return (error);
107882cd038dSYoshinobu Inoue 	}
1079a7028af7SDavid Greenman 
1080df8bae1dSRodney W. Grimes 	case SIOCADDMULTI:
1081df8bae1dSRodney W. Grimes 	case SIOCDELMULTI:
1082f711d546SPoul-Henning Kamp 		error = suser(p);
10839448326fSPoul-Henning Kamp 		if (error)
1084df8bae1dSRodney W. Grimes 			return (error);
1085477180fbSGarrett Wollman 
1086477180fbSGarrett Wollman 		/* Don't allow group membership on non-multicast interfaces. */
1087477180fbSGarrett Wollman 		if ((ifp->if_flags & IFF_MULTICAST) == 0)
1088477180fbSGarrett Wollman 			return EOPNOTSUPP;
1089477180fbSGarrett Wollman 
1090477180fbSGarrett Wollman 		/* Don't let users screw up protocols' entries. */
1091477180fbSGarrett Wollman 		if (ifr->ifr_addr.sa_family != AF_LINK)
1092477180fbSGarrett Wollman 			return EINVAL;
1093477180fbSGarrett Wollman 
1094477180fbSGarrett Wollman 		if (cmd == SIOCADDMULTI) {
1095477180fbSGarrett Wollman 			struct ifmultiaddr *ifma;
1096477180fbSGarrett Wollman 			error = if_addmulti(ifp, &ifr->ifr_addr, &ifma);
1097477180fbSGarrett Wollman 		} else {
1098477180fbSGarrett Wollman 			error = if_delmulti(ifp, &ifr->ifr_addr);
1099477180fbSGarrett Wollman 		}
1100e39a0280SGary Palmer 		if (error == 0)
110198b9590eSPoul-Henning Kamp 			getmicrotime(&ifp->if_lastchange);
1102477180fbSGarrett Wollman 		return error;
1103df8bae1dSRodney W. Grimes 
110441b3e8e5SJun-ichiro itojun Hagino 	case SIOCSIFPHYADDR:
110541b3e8e5SJun-ichiro itojun Hagino 	case SIOCDIFPHYADDR:
110641b3e8e5SJun-ichiro itojun Hagino #ifdef INET6
110741b3e8e5SJun-ichiro itojun Hagino 	case SIOCSIFPHYADDR_IN6:
110841b3e8e5SJun-ichiro itojun Hagino #endif
110933841545SHajimu UMEMOTO 	case SIOCSLIFPHYADDR:
1110a912e453SPeter Wemm         case SIOCSIFMEDIA:
1111d7189ec6SJoerg Wunsch 	case SIOCSIFGENERIC:
1112f711d546SPoul-Henning Kamp 		error = suser(p);
1113a912e453SPeter Wemm 		if (error)
1114a912e453SPeter Wemm 			return (error);
1115a912e453SPeter Wemm 		if (ifp->if_ioctl == 0)
1116a912e453SPeter Wemm 			return (EOPNOTSUPP);
1117a912e453SPeter Wemm 		error = (*ifp->if_ioctl)(ifp, cmd, data);
1118a912e453SPeter Wemm 		if (error == 0)
111998b9590eSPoul-Henning Kamp 			getmicrotime(&ifp->if_lastchange);
1120a912e453SPeter Wemm 		return error;
1121a912e453SPeter Wemm 
1122413dd0baSPoul-Henning Kamp 	case SIOCGIFSTATUS:
1123413dd0baSPoul-Henning Kamp 		ifs = (struct ifstat *)data;
1124413dd0baSPoul-Henning Kamp 		ifs->ascii[0] = '\0';
1125413dd0baSPoul-Henning Kamp 
112633841545SHajimu UMEMOTO 	case SIOCGIFPSRCADDR:
112733841545SHajimu UMEMOTO 	case SIOCGIFPDSTADDR:
112833841545SHajimu UMEMOTO 	case SIOCGLIFPHYADDR:
1129a912e453SPeter Wemm 	case SIOCGIFMEDIA:
1130d7189ec6SJoerg Wunsch 	case SIOCGIFGENERIC:
1131a912e453SPeter Wemm 		if (ifp->if_ioctl == 0)
1132a912e453SPeter Wemm 			return (EOPNOTSUPP);
1133a912e453SPeter Wemm 		return ((*ifp->if_ioctl)(ifp, cmd, data));
1134a912e453SPeter Wemm 
1135b106252cSBill Paul 	case SIOCSIFLLADDR:
1136b106252cSBill Paul 		error = suser(p);
1137b106252cSBill Paul 		if (error)
1138b106252cSBill Paul 			return (error);
113966ce51ceSArchie Cobbs 		return if_setlladdr(ifp,
114066ce51ceSArchie Cobbs 		    ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len);
114166ce51ceSArchie Cobbs 
1142df8bae1dSRodney W. Grimes 	default:
114382cd038dSYoshinobu Inoue 		oif_flags = ifp->if_flags;
1144df8bae1dSRodney W. Grimes 		if (so->so_proto == 0)
1145df8bae1dSRodney W. Grimes 			return (EOPNOTSUPP);
1146df8bae1dSRodney W. Grimes #ifndef COMPAT_43
114782cd038dSYoshinobu Inoue 		error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd,
11482c37256eSGarrett Wollman 								 data,
1149bc6d9b80SJoerg Wunsch 								 ifp, p));
1150df8bae1dSRodney W. Grimes #else
1151df8bae1dSRodney W. Grimes 	    {
1152df8bae1dSRodney W. Grimes 		int ocmd = cmd;
1153df8bae1dSRodney W. Grimes 
1154df8bae1dSRodney W. Grimes 		switch (cmd) {
1155df8bae1dSRodney W. Grimes 
1156df8bae1dSRodney W. Grimes 		case SIOCSIFDSTADDR:
1157df8bae1dSRodney W. Grimes 		case SIOCSIFADDR:
1158df8bae1dSRodney W. Grimes 		case SIOCSIFBRDADDR:
1159df8bae1dSRodney W. Grimes 		case SIOCSIFNETMASK:
1160df8bae1dSRodney W. Grimes #if BYTE_ORDER != BIG_ENDIAN
1161df8bae1dSRodney W. Grimes 			if (ifr->ifr_addr.sa_family == 0 &&
1162df8bae1dSRodney W. Grimes 			    ifr->ifr_addr.sa_len < 16) {
1163df8bae1dSRodney W. Grimes 				ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len;
1164df8bae1dSRodney W. Grimes 				ifr->ifr_addr.sa_len = 16;
1165df8bae1dSRodney W. Grimes 			}
1166df8bae1dSRodney W. Grimes #else
1167df8bae1dSRodney W. Grimes 			if (ifr->ifr_addr.sa_len == 0)
1168df8bae1dSRodney W. Grimes 				ifr->ifr_addr.sa_len = 16;
1169df8bae1dSRodney W. Grimes #endif
1170df8bae1dSRodney W. Grimes 			break;
1171df8bae1dSRodney W. Grimes 
1172df8bae1dSRodney W. Grimes 		case OSIOCGIFADDR:
1173df8bae1dSRodney W. Grimes 			cmd = SIOCGIFADDR;
1174df8bae1dSRodney W. Grimes 			break;
1175df8bae1dSRodney W. Grimes 
1176df8bae1dSRodney W. Grimes 		case OSIOCGIFDSTADDR:
1177df8bae1dSRodney W. Grimes 			cmd = SIOCGIFDSTADDR;
1178df8bae1dSRodney W. Grimes 			break;
1179df8bae1dSRodney W. Grimes 
1180df8bae1dSRodney W. Grimes 		case OSIOCGIFBRDADDR:
1181df8bae1dSRodney W. Grimes 			cmd = SIOCGIFBRDADDR;
1182df8bae1dSRodney W. Grimes 			break;
1183df8bae1dSRodney W. Grimes 
1184df8bae1dSRodney W. Grimes 		case OSIOCGIFNETMASK:
1185df8bae1dSRodney W. Grimes 			cmd = SIOCGIFNETMASK;
1186df8bae1dSRodney W. Grimes 		}
11872c37256eSGarrett Wollman 		error =  ((*so->so_proto->pr_usrreqs->pru_control)(so,
11882c37256eSGarrett Wollman 								   cmd,
11892c37256eSGarrett Wollman 								   data,
1190a29f300eSGarrett Wollman 								   ifp, p));
1191df8bae1dSRodney W. Grimes 		switch (ocmd) {
1192df8bae1dSRodney W. Grimes 
1193df8bae1dSRodney W. Grimes 		case OSIOCGIFADDR:
1194df8bae1dSRodney W. Grimes 		case OSIOCGIFDSTADDR:
1195df8bae1dSRodney W. Grimes 		case OSIOCGIFBRDADDR:
1196df8bae1dSRodney W. Grimes 		case OSIOCGIFNETMASK:
1197df8bae1dSRodney W. Grimes 			*(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family;
119882cd038dSYoshinobu Inoue 
119982cd038dSYoshinobu Inoue 		}
120082cd038dSYoshinobu Inoue 	    }
120182cd038dSYoshinobu Inoue #endif /* COMPAT_43 */
120282cd038dSYoshinobu Inoue 
120382cd038dSYoshinobu Inoue 		if ((oif_flags ^ ifp->if_flags) & IFF_UP) {
120482cd038dSYoshinobu Inoue #ifdef INET6
12053411310dSYoshinobu Inoue 			DELAY(100);/* XXX: temporal workaround for fxp issue*/
120682cd038dSYoshinobu Inoue 			if (ifp->if_flags & IFF_UP) {
120782cd038dSYoshinobu Inoue 				int s = splimp();
120882cd038dSYoshinobu Inoue 				in6_if_up(ifp);
120982cd038dSYoshinobu Inoue 				splx(s);
121082cd038dSYoshinobu Inoue 			}
121182cd038dSYoshinobu Inoue #endif
1212df8bae1dSRodney W. Grimes 		}
1213df8bae1dSRodney W. Grimes 		return (error);
1214df8bae1dSRodney W. Grimes 
1215df8bae1dSRodney W. Grimes 	}
1216df8bae1dSRodney W. Grimes 	return (0);
1217df8bae1dSRodney W. Grimes }
1218df8bae1dSRodney W. Grimes 
1219df8bae1dSRodney W. Grimes /*
1220963e4c2aSGarrett Wollman  * Set/clear promiscuous mode on interface ifp based on the truth value
1221963e4c2aSGarrett Wollman  * of pswitch.  The calls are reference counted so that only the first
1222963e4c2aSGarrett Wollman  * "on" request actually has an effect, as does the final "off" request.
1223963e4c2aSGarrett Wollman  * Results are undefined if the "off" and "on" requests are not matched.
1224963e4c2aSGarrett Wollman  */
1225963e4c2aSGarrett Wollman int
1226963e4c2aSGarrett Wollman ifpromisc(ifp, pswitch)
1227963e4c2aSGarrett Wollman 	struct ifnet *ifp;
1228963e4c2aSGarrett Wollman 	int pswitch;
1229963e4c2aSGarrett Wollman {
1230963e4c2aSGarrett Wollman 	struct ifreq ifr;
12314a26224cSGarrett Wollman 	int error;
12324f3c11a6SBill Fenner 	int oldflags, oldpcount;
1233963e4c2aSGarrett Wollman 
12344f3c11a6SBill Fenner 	oldpcount = ifp->if_pcount;
12352c514a31SBrian Somers 	oldflags = ifp->if_flags;
1236963e4c2aSGarrett Wollman 	if (pswitch) {
1237963e4c2aSGarrett Wollman 		/*
1238963e4c2aSGarrett Wollman 		 * If the device is not configured up, we cannot put it in
1239963e4c2aSGarrett Wollman 		 * promiscuous mode.
1240963e4c2aSGarrett Wollman 		 */
1241963e4c2aSGarrett Wollman 		if ((ifp->if_flags & IFF_UP) == 0)
1242963e4c2aSGarrett Wollman 			return (ENETDOWN);
1243963e4c2aSGarrett Wollman 		if (ifp->if_pcount++ != 0)
1244963e4c2aSGarrett Wollman 			return (0);
1245963e4c2aSGarrett Wollman 		ifp->if_flags |= IFF_PROMISC;
1246963e4c2aSGarrett Wollman 	} else {
1247963e4c2aSGarrett Wollman 		if (--ifp->if_pcount > 0)
1248963e4c2aSGarrett Wollman 			return (0);
1249963e4c2aSGarrett Wollman 		ifp->if_flags &= ~IFF_PROMISC;
1250963e4c2aSGarrett Wollman 	}
1251963e4c2aSGarrett Wollman 	ifr.ifr_flags = ifp->if_flags;
12524a26224cSGarrett Wollman 	error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr);
12534f3c11a6SBill Fenner 	if (error == 0) {
12544f3c11a6SBill Fenner 		log(LOG_INFO, "%s%d: promiscuous mode %s\n",
12554f3c11a6SBill Fenner 		    ifp->if_name, ifp->if_unit,
12564f3c11a6SBill Fenner 		    (ifp->if_flags & IFF_PROMISC) ? "enabled" : "disabled");
12574a26224cSGarrett Wollman 		rt_ifmsg(ifp);
12584f3c11a6SBill Fenner 	} else {
12594f3c11a6SBill Fenner 		ifp->if_pcount = oldpcount;
12602c514a31SBrian Somers 		ifp->if_flags = oldflags;
12614f3c11a6SBill Fenner 	}
12624a26224cSGarrett Wollman 	return error;
1263963e4c2aSGarrett Wollman }
1264963e4c2aSGarrett Wollman 
1265963e4c2aSGarrett Wollman /*
1266df8bae1dSRodney W. Grimes  * Return interface configuration
1267df8bae1dSRodney W. Grimes  * of system.  List may be used
1268df8bae1dSRodney W. Grimes  * in later ioctl's (above) to get
1269df8bae1dSRodney W. Grimes  * other information.
1270df8bae1dSRodney W. Grimes  */
1271df8bae1dSRodney W. Grimes /*ARGSUSED*/
12723bda9f9bSPoul-Henning Kamp static int
1273df8bae1dSRodney W. Grimes ifconf(cmd, data)
1274ecbb00a2SDoug Rabson 	u_long cmd;
1275df8bae1dSRodney W. Grimes 	caddr_t data;
1276df8bae1dSRodney W. Grimes {
12770b59d917SJonathan Lemon 	struct ifconf *ifc = (struct ifconf *)data;
12780b59d917SJonathan Lemon 	struct ifnet *ifp;
12790b59d917SJonathan Lemon 	struct ifaddr *ifa;
1280df8bae1dSRodney W. Grimes 	struct ifreq ifr, *ifrp;
1281df8bae1dSRodney W. Grimes 	int space = ifc->ifc_len, error = 0;
1282df8bae1dSRodney W. Grimes 
1283df8bae1dSRodney W. Grimes 	ifrp = ifc->ifc_req;
12840b59d917SJonathan Lemon 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
12851ce9bf88SPoul-Henning Kamp 		char workbuf[64];
128675c13541SPoul-Henning Kamp 		int ifnlen, addrs;
12872624cf89SGarrett Wollman 
12880b59d917SJonathan Lemon 		if (space > sizeof(ifr))
12890b59d917SJonathan Lemon 			break;
12900b59d917SJonathan Lemon 
12912127f260SArchie Cobbs 		ifnlen = snprintf(workbuf, sizeof(workbuf),
12922127f260SArchie Cobbs 		    "%s%d", ifp->if_name, ifp->if_unit);
12931ce9bf88SPoul-Henning Kamp 		if(ifnlen + 1 > sizeof ifr.ifr_name) {
12942624cf89SGarrett Wollman 			error = ENAMETOOLONG;
1295b3f1e629SGuido van Rooij 			break;
12962624cf89SGarrett Wollman 		} else {
12971ce9bf88SPoul-Henning Kamp 			strcpy(ifr.ifr_name, workbuf);
12982624cf89SGarrett Wollman 		}
12992624cf89SGarrett Wollman 
130075c13541SPoul-Henning Kamp 		addrs = 0;
130122f29826SPoul-Henning Kamp 		ifa = TAILQ_FIRST(&ifp->if_addrhead);
130259562606SGarrett Wollman 		for ( ; space > sizeof (ifr) && ifa;
130322f29826SPoul-Henning Kamp 		    ifa = TAILQ_NEXT(ifa, ifa_link)) {
1304df8bae1dSRodney W. Grimes 			register struct sockaddr *sa = ifa->ifa_addr;
130591421ba2SRobert Watson 			if (jailed(curproc->p_ucred) &&
130691421ba2SRobert Watson 			    prison_if(curproc->p_ucred, sa))
130775c13541SPoul-Henning Kamp 				continue;
130875c13541SPoul-Henning Kamp 			addrs++;
1309df8bae1dSRodney W. Grimes #ifdef COMPAT_43
1310df8bae1dSRodney W. Grimes 			if (cmd == OSIOCGIFCONF) {
1311df8bae1dSRodney W. Grimes 				struct osockaddr *osa =
1312df8bae1dSRodney W. Grimes 					 (struct osockaddr *)&ifr.ifr_addr;
1313df8bae1dSRodney W. Grimes 				ifr.ifr_addr = *sa;
1314df8bae1dSRodney W. Grimes 				osa->sa_family = sa->sa_family;
1315df8bae1dSRodney W. Grimes 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
1316df8bae1dSRodney W. Grimes 						sizeof (ifr));
1317df8bae1dSRodney W. Grimes 				ifrp++;
1318df8bae1dSRodney W. Grimes 			} else
1319df8bae1dSRodney W. Grimes #endif
1320df8bae1dSRodney W. Grimes 			if (sa->sa_len <= sizeof(*sa)) {
1321df8bae1dSRodney W. Grimes 				ifr.ifr_addr = *sa;
1322df8bae1dSRodney W. Grimes 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
1323df8bae1dSRodney W. Grimes 						sizeof (ifr));
1324df8bae1dSRodney W. Grimes 				ifrp++;
1325df8bae1dSRodney W. Grimes 			} else {
1326d91a068eSGuido van Rooij 				if (space < sizeof (ifr) + sa->sa_len -
1327d91a068eSGuido van Rooij 					    sizeof(*sa))
1328b3f1e629SGuido van Rooij 					break;
1329df8bae1dSRodney W. Grimes 				space -= sa->sa_len - sizeof(*sa);
1330df8bae1dSRodney W. Grimes 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
1331df8bae1dSRodney W. Grimes 						sizeof (ifr.ifr_name));
1332df8bae1dSRodney W. Grimes 				if (error == 0)
1333df8bae1dSRodney W. Grimes 				    error = copyout((caddr_t)sa,
1334df8bae1dSRodney W. Grimes 				      (caddr_t)&ifrp->ifr_addr, sa->sa_len);
1335df8bae1dSRodney W. Grimes 				ifrp = (struct ifreq *)
1336df8bae1dSRodney W. Grimes 					(sa->sa_len + (caddr_t)&ifrp->ifr_addr);
1337df8bae1dSRodney W. Grimes 			}
1338df8bae1dSRodney W. Grimes 			if (error)
1339df8bae1dSRodney W. Grimes 				break;
1340df8bae1dSRodney W. Grimes 			space -= sizeof (ifr);
1341df8bae1dSRodney W. Grimes 		}
1342b3f1e629SGuido van Rooij 		if (error)
1343b3f1e629SGuido van Rooij 			break;
134475c13541SPoul-Henning Kamp 		if (!addrs) {
134575c13541SPoul-Henning Kamp 			bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
134675c13541SPoul-Henning Kamp 			error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
134775c13541SPoul-Henning Kamp 			    sizeof (ifr));
134875c13541SPoul-Henning Kamp 			if (error)
134975c13541SPoul-Henning Kamp 				break;
1350b3f1e629SGuido van Rooij 			space -= sizeof (ifr);
1351b3f1e629SGuido van Rooij 			ifrp++;
135275c13541SPoul-Henning Kamp 		}
1353df8bae1dSRodney W. Grimes 	}
1354df8bae1dSRodney W. Grimes 	ifc->ifc_len -= space;
1355df8bae1dSRodney W. Grimes 	return (error);
1356df8bae1dSRodney W. Grimes }
1357df8bae1dSRodney W. Grimes 
13581158dfb7SGarrett Wollman /*
13591158dfb7SGarrett Wollman  * Just like if_promisc(), but for all-multicast-reception mode.
13601158dfb7SGarrett Wollman  */
13611158dfb7SGarrett Wollman int
13621158dfb7SGarrett Wollman if_allmulti(ifp, onswitch)
13631158dfb7SGarrett Wollman 	struct ifnet *ifp;
13641158dfb7SGarrett Wollman 	int onswitch;
13651158dfb7SGarrett Wollman {
13661158dfb7SGarrett Wollman 	int error = 0;
13671158dfb7SGarrett Wollman 	int s = splimp();
13681158dfb7SGarrett Wollman 
13691158dfb7SGarrett Wollman 	if (onswitch) {
13701158dfb7SGarrett Wollman 		if (ifp->if_amcount++ == 0) {
13711158dfb7SGarrett Wollman 			ifp->if_flags |= IFF_ALLMULTI;
13721158dfb7SGarrett Wollman 			error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0);
13731158dfb7SGarrett Wollman 		}
13741158dfb7SGarrett Wollman 	} else {
13751158dfb7SGarrett Wollman 		if (ifp->if_amcount > 1) {
13761158dfb7SGarrett Wollman 			ifp->if_amcount--;
13771158dfb7SGarrett Wollman 		} else {
13781158dfb7SGarrett Wollman 			ifp->if_amcount = 0;
13791158dfb7SGarrett Wollman 			ifp->if_flags &= ~IFF_ALLMULTI;
13801158dfb7SGarrett Wollman 			error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0);
13811158dfb7SGarrett Wollman 		}
13821158dfb7SGarrett Wollman 	}
13831158dfb7SGarrett Wollman 	splx(s);
13844a26224cSGarrett Wollman 
13854a26224cSGarrett Wollman 	if (error == 0)
13864a26224cSGarrett Wollman 		rt_ifmsg(ifp);
13871158dfb7SGarrett Wollman 	return error;
13881158dfb7SGarrett Wollman }
13891158dfb7SGarrett Wollman 
13901158dfb7SGarrett Wollman /*
13911158dfb7SGarrett Wollman  * Add a multicast listenership to the interface in question.
13921158dfb7SGarrett Wollman  * The link layer provides a routine which converts
13931158dfb7SGarrett Wollman  */
13941158dfb7SGarrett Wollman int
1395373f88edSGarrett Wollman if_addmulti(ifp, sa, retifma)
13961158dfb7SGarrett Wollman 	struct ifnet *ifp;	/* interface to manipulate */
13971158dfb7SGarrett Wollman 	struct sockaddr *sa;	/* address to add */
1398b2053118SGarrett Wollman 	struct ifmultiaddr **retifma;
13991158dfb7SGarrett Wollman {
14001158dfb7SGarrett Wollman 	struct sockaddr *llsa, *dupsa;
14011158dfb7SGarrett Wollman 	int error, s;
14021158dfb7SGarrett Wollman 	struct ifmultiaddr *ifma;
14031158dfb7SGarrett Wollman 
140457af7922SJulian Elischer 	/*
140557af7922SJulian Elischer 	 * If the matching multicast address already exists
140657af7922SJulian Elischer 	 * then don't add a new one, just add a reference
140757af7922SJulian Elischer 	 */
14086817526dSPoul-Henning Kamp 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
140957af7922SJulian Elischer 		if (equal(sa, ifma->ifma_addr)) {
14101158dfb7SGarrett Wollman 			ifma->ifma_refcount++;
141157af7922SJulian Elischer 			if (retifma)
141257af7922SJulian Elischer 				*retifma = ifma;
14131158dfb7SGarrett Wollman 			return 0;
14141158dfb7SGarrett Wollman 		}
141557af7922SJulian Elischer 	}
14161158dfb7SGarrett Wollman 
14171158dfb7SGarrett Wollman 	/*
14181158dfb7SGarrett Wollman 	 * Give the link layer a chance to accept/reject it, and also
14191158dfb7SGarrett Wollman 	 * find out which AF_LINK address this maps to, if it isn't one
14201158dfb7SGarrett Wollman 	 * already.
14211158dfb7SGarrett Wollman 	 */
14221158dfb7SGarrett Wollman 	if (ifp->if_resolvemulti) {
14231158dfb7SGarrett Wollman 		error = ifp->if_resolvemulti(ifp, &llsa, sa);
14241158dfb7SGarrett Wollman 		if (error) return error;
14251158dfb7SGarrett Wollman 	} else {
14261158dfb7SGarrett Wollman 		llsa = 0;
14271158dfb7SGarrett Wollman 	}
14281158dfb7SGarrett Wollman 
14291158dfb7SGarrett Wollman 	MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK);
14301158dfb7SGarrett Wollman 	MALLOC(dupsa, struct sockaddr *, sa->sa_len, M_IFMADDR, M_WAITOK);
14311158dfb7SGarrett Wollman 	bcopy(sa, dupsa, sa->sa_len);
14321158dfb7SGarrett Wollman 
14331158dfb7SGarrett Wollman 	ifma->ifma_addr = dupsa;
14341158dfb7SGarrett Wollman 	ifma->ifma_lladdr = llsa;
14351158dfb7SGarrett Wollman 	ifma->ifma_ifp = ifp;
14361158dfb7SGarrett Wollman 	ifma->ifma_refcount = 1;
1437373f88edSGarrett Wollman 	ifma->ifma_protospec = 0;
1438477180fbSGarrett Wollman 	rt_newmaddrmsg(RTM_NEWMADDR, ifma);
1439373f88edSGarrett Wollman 
14401158dfb7SGarrett Wollman 	/*
14411158dfb7SGarrett Wollman 	 * Some network interfaces can scan the address list at
14421158dfb7SGarrett Wollman 	 * interrupt time; lock them out.
14431158dfb7SGarrett Wollman 	 */
14441158dfb7SGarrett Wollman 	s = splimp();
14456817526dSPoul-Henning Kamp 	TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link);
14461158dfb7SGarrett Wollman 	splx(s);
1447373f88edSGarrett Wollman 	*retifma = ifma;
14481158dfb7SGarrett Wollman 
14491158dfb7SGarrett Wollman 	if (llsa != 0) {
14506817526dSPoul-Henning Kamp 		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
14511158dfb7SGarrett Wollman 			if (equal(ifma->ifma_addr, llsa))
14521158dfb7SGarrett Wollman 				break;
14531158dfb7SGarrett Wollman 		}
14541158dfb7SGarrett Wollman 		if (ifma) {
14551158dfb7SGarrett Wollman 			ifma->ifma_refcount++;
14561158dfb7SGarrett Wollman 		} else {
14571158dfb7SGarrett Wollman 			MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma,
14581158dfb7SGarrett Wollman 			       M_IFMADDR, M_WAITOK);
1459477180fbSGarrett Wollman 			MALLOC(dupsa, struct sockaddr *, llsa->sa_len,
1460477180fbSGarrett Wollman 			       M_IFMADDR, M_WAITOK);
1461477180fbSGarrett Wollman 			bcopy(llsa, dupsa, llsa->sa_len);
1462477180fbSGarrett Wollman 			ifma->ifma_addr = dupsa;
14631158dfb7SGarrett Wollman 			ifma->ifma_ifp = ifp;
14641158dfb7SGarrett Wollman 			ifma->ifma_refcount = 1;
14651158dfb7SGarrett Wollman 			s = splimp();
14666817526dSPoul-Henning Kamp 			TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link);
14671158dfb7SGarrett Wollman 			splx(s);
14681158dfb7SGarrett Wollman 		}
146957af7922SJulian Elischer 	}
14701158dfb7SGarrett Wollman 	/*
14711158dfb7SGarrett Wollman 	 * We are certain we have added something, so call down to the
14721158dfb7SGarrett Wollman 	 * interface to let them know about it.
14731158dfb7SGarrett Wollman 	 */
14741158dfb7SGarrett Wollman 	s = splimp();
14751158dfb7SGarrett Wollman 	ifp->if_ioctl(ifp, SIOCADDMULTI, 0);
14761158dfb7SGarrett Wollman 	splx(s);
14771158dfb7SGarrett Wollman 
14781158dfb7SGarrett Wollman 	return 0;
14791158dfb7SGarrett Wollman }
14801158dfb7SGarrett Wollman 
14811158dfb7SGarrett Wollman /*
14821158dfb7SGarrett Wollman  * Remove a reference to a multicast address on this interface.  Yell
14831158dfb7SGarrett Wollman  * if the request does not match an existing membership.
14841158dfb7SGarrett Wollman  */
14851158dfb7SGarrett Wollman int
14861158dfb7SGarrett Wollman if_delmulti(ifp, sa)
14871158dfb7SGarrett Wollman 	struct ifnet *ifp;
14881158dfb7SGarrett Wollman 	struct sockaddr *sa;
14891158dfb7SGarrett Wollman {
14901158dfb7SGarrett Wollman 	struct ifmultiaddr *ifma;
14911158dfb7SGarrett Wollman 	int s;
14921158dfb7SGarrett Wollman 
14936817526dSPoul-Henning Kamp 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
14941158dfb7SGarrett Wollman 		if (equal(sa, ifma->ifma_addr))
14951158dfb7SGarrett Wollman 			break;
14961158dfb7SGarrett Wollman 	if (ifma == 0)
14971158dfb7SGarrett Wollman 		return ENOENT;
14981158dfb7SGarrett Wollman 
14991158dfb7SGarrett Wollman 	if (ifma->ifma_refcount > 1) {
15001158dfb7SGarrett Wollman 		ifma->ifma_refcount--;
15011158dfb7SGarrett Wollman 		return 0;
15021158dfb7SGarrett Wollman 	}
15031158dfb7SGarrett Wollman 
1504477180fbSGarrett Wollman 	rt_newmaddrmsg(RTM_DELMADDR, ifma);
15051158dfb7SGarrett Wollman 	sa = ifma->ifma_lladdr;
15061158dfb7SGarrett Wollman 	s = splimp();
15076817526dSPoul-Henning Kamp 	TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
1508ccb7cc8dSYaroslav Tykhiy 	/*
1509ccb7cc8dSYaroslav Tykhiy 	 * Make sure the interface driver is notified
1510ccb7cc8dSYaroslav Tykhiy 	 * in the case of a link layer mcast group being left.
1511ccb7cc8dSYaroslav Tykhiy 	 */
1512ccb7cc8dSYaroslav Tykhiy 	if (ifma->ifma_addr->sa_family == AF_LINK && sa == 0)
1513ccb7cc8dSYaroslav Tykhiy 		ifp->if_ioctl(ifp, SIOCDELMULTI, 0);
15141158dfb7SGarrett Wollman 	splx(s);
15151158dfb7SGarrett Wollman 	free(ifma->ifma_addr, M_IFMADDR);
15161158dfb7SGarrett Wollman 	free(ifma, M_IFMADDR);
15171158dfb7SGarrett Wollman 	if (sa == 0)
15181158dfb7SGarrett Wollman 		return 0;
15191158dfb7SGarrett Wollman 
15201158dfb7SGarrett Wollman 	/*
15211158dfb7SGarrett Wollman 	 * Now look for the link-layer address which corresponds to
15221158dfb7SGarrett Wollman 	 * this network address.  It had been squirreled away in
15231158dfb7SGarrett Wollman 	 * ifma->ifma_lladdr for this purpose (so we don't have
15241158dfb7SGarrett Wollman 	 * to call ifp->if_resolvemulti() again), and we saved that
15251158dfb7SGarrett Wollman 	 * value in sa above.  If some nasty deleted the
15261158dfb7SGarrett Wollman 	 * link-layer address out from underneath us, we can deal because
15271158dfb7SGarrett Wollman 	 * the address we stored was is not the same as the one which was
15281158dfb7SGarrett Wollman 	 * in the record for the link-layer address.  (So we don't complain
15291158dfb7SGarrett Wollman 	 * in that case.)
15301158dfb7SGarrett Wollman 	 */
15316817526dSPoul-Henning Kamp 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
15321158dfb7SGarrett Wollman 		if (equal(sa, ifma->ifma_addr))
15331158dfb7SGarrett Wollman 			break;
15341158dfb7SGarrett Wollman 	if (ifma == 0)
15351158dfb7SGarrett Wollman 		return 0;
15361158dfb7SGarrett Wollman 
15371158dfb7SGarrett Wollman 	if (ifma->ifma_refcount > 1) {
15381158dfb7SGarrett Wollman 		ifma->ifma_refcount--;
15391158dfb7SGarrett Wollman 		return 0;
15401158dfb7SGarrett Wollman 	}
15411158dfb7SGarrett Wollman 
15421158dfb7SGarrett Wollman 	s = splimp();
15436817526dSPoul-Henning Kamp 	TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
1544c7323482SBill Paul 	ifp->if_ioctl(ifp, SIOCDELMULTI, 0);
15451158dfb7SGarrett Wollman 	splx(s);
15461158dfb7SGarrett Wollman 	free(ifma->ifma_addr, M_IFMADDR);
15471158dfb7SGarrett Wollman 	free(sa, M_IFMADDR);
15481158dfb7SGarrett Wollman 	free(ifma, M_IFMADDR);
15491158dfb7SGarrett Wollman 
15501158dfb7SGarrett Wollman 	return 0;
15511158dfb7SGarrett Wollman }
15521158dfb7SGarrett Wollman 
155366ce51ceSArchie Cobbs /*
155466ce51ceSArchie Cobbs  * Set the link layer address on an interface.
155566ce51ceSArchie Cobbs  *
155666ce51ceSArchie Cobbs  * At this time we only support certain types of interfaces,
155766ce51ceSArchie Cobbs  * and we don't allow the length of the address to change.
155866ce51ceSArchie Cobbs  */
155966ce51ceSArchie Cobbs int
156066ce51ceSArchie Cobbs if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len)
156166ce51ceSArchie Cobbs {
156266ce51ceSArchie Cobbs 	struct sockaddr_dl *sdl;
156366ce51ceSArchie Cobbs 	struct ifaddr *ifa;
156466ce51ceSArchie Cobbs 
1565f9132cebSJonathan Lemon 	ifa = ifaddr_byindex(ifp->if_index);
156666ce51ceSArchie Cobbs 	if (ifa == NULL)
156766ce51ceSArchie Cobbs 		return (EINVAL);
156866ce51ceSArchie Cobbs 	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
156966ce51ceSArchie Cobbs 	if (sdl == NULL)
157066ce51ceSArchie Cobbs 		return (EINVAL);
157166ce51ceSArchie Cobbs 	if (len != sdl->sdl_alen)	/* don't allow length to change */
157266ce51ceSArchie Cobbs 		return (EINVAL);
157366ce51ceSArchie Cobbs 	switch (ifp->if_type) {
157466ce51ceSArchie Cobbs 	case IFT_ETHER:			/* these types use struct arpcom */
157566ce51ceSArchie Cobbs 	case IFT_FDDI:
157666ce51ceSArchie Cobbs 	case IFT_XETHER:
157766ce51ceSArchie Cobbs 	case IFT_ISO88025:
1578b7bffa71SYaroslav Tykhiy 	case IFT_L2VLAN:
157966ce51ceSArchie Cobbs 		bcopy(lladdr, ((struct arpcom *)ifp->if_softc)->ac_enaddr, len);
158066ce51ceSArchie Cobbs 		bcopy(lladdr, LLADDR(sdl), len);
158166ce51ceSArchie Cobbs 		break;
158266ce51ceSArchie Cobbs 	default:
158366ce51ceSArchie Cobbs 		return (ENODEV);
158466ce51ceSArchie Cobbs 	}
158566ce51ceSArchie Cobbs 	/*
158666ce51ceSArchie Cobbs 	 * If the interface is already up, we need
158766ce51ceSArchie Cobbs 	 * to re-init it in order to reprogram its
158866ce51ceSArchie Cobbs 	 * address filter.
158966ce51ceSArchie Cobbs 	 */
159066ce51ceSArchie Cobbs 	if ((ifp->if_flags & IFF_UP) != 0) {
159166ce51ceSArchie Cobbs 		ifp->if_flags &= ~IFF_UP;
159266ce51ceSArchie Cobbs 		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, NULL);
159366ce51ceSArchie Cobbs 		ifp->if_flags |= IFF_UP;
159466ce51ceSArchie Cobbs 		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, NULL);
159566ce51ceSArchie Cobbs 	}
159666ce51ceSArchie Cobbs 	return (0);
159766ce51ceSArchie Cobbs }
159866ce51ceSArchie Cobbs 
1599373f88edSGarrett Wollman struct ifmultiaddr *
1600373f88edSGarrett Wollman ifmaof_ifpforaddr(sa, ifp)
1601373f88edSGarrett Wollman 	struct sockaddr *sa;
1602373f88edSGarrett Wollman 	struct ifnet *ifp;
1603373f88edSGarrett Wollman {
1604373f88edSGarrett Wollman 	struct ifmultiaddr *ifma;
1605373f88edSGarrett Wollman 
16066817526dSPoul-Henning Kamp 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
1607373f88edSGarrett Wollman 		if (equal(ifma->ifma_addr, sa))
1608373f88edSGarrett Wollman 			break;
1609373f88edSGarrett Wollman 
1610373f88edSGarrett Wollman 	return ifma;
1611373f88edSGarrett Wollman }
1612373f88edSGarrett Wollman 
1613602d513cSGarrett Wollman SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers");
16142c37256eSGarrett Wollman SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management");
1615