xref: /netbsd-src/sys/net/if.c (revision 95d875fb90b1458e4f1de6950286ddcd6644bc61)
1 /*	$NetBSD: if.c,v 1.52 1999/09/29 22:42:02 thorpej Exp $	*/
2 
3 /*
4  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1980, 1986, 1993
34  *	The Regents of the University of California.  All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. All advertising materials mentioning features or use of this software
45  *    must display the following acknowledgement:
46  *	This product includes software developed by the University of
47  *	California, Berkeley and its contributors.
48  * 4. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  *
64  *	@(#)if.c	8.5 (Berkeley) 1/9/95
65  */
66 
67 #include "opt_inet.h"
68 
69 #include "opt_compat_linux.h"
70 #include "opt_compat_svr4.h"
71 #include "opt_compat_43.h"
72 #include "opt_atalk.h"
73 
74 #include <sys/param.h>
75 #include <sys/mbuf.h>
76 #include <sys/systm.h>
77 #include <sys/proc.h>
78 #include <sys/socket.h>
79 #include <sys/socketvar.h>
80 #include <sys/protosw.h>
81 #include <sys/kernel.h>
82 #include <sys/ioctl.h>
83 
84 #include <net/if.h>
85 #include <net/if_dl.h>
86 #include <net/if_types.h>
87 #include <net/radix.h>
88 #ifdef NETATALK
89 #include <netatalk/at_extern.h>
90 #include <netatalk/at.h>
91 #endif
92 
93 #ifdef INET6
94 /*XXX*/
95 #include <netinet/in.h>
96 #endif
97 
98 int	ifqmaxlen = IFQ_MAXLEN;
99 void	if_slowtimo __P((void *arg));
100 
101 #ifdef INET6
102 /*
103  * XXX: declare here to avoid to include many inet6 related files..
104  * should be more generalized?
105  */
106 extern void nd6_setmtu __P((struct ifnet *));
107 #endif
108 
109 /*
110  * Network interface utility routines.
111  *
112  * Routines with ifa_ifwith* names take sockaddr *'s as
113  * parameters.
114  */
115 void
116 ifinit()
117 {
118 
119 	if_slowtimo(NULL);
120 }
121 
122 int if_index = 0;
123 struct ifaddr **ifnet_addrs = NULL;
124 struct ifnet **ifindex2ifnet = NULL;
125 
126 /*
127  * Attach an interface to the
128  * list of "active" interfaces.
129  */
130 void
131 if_attach(ifp)
132 	struct ifnet *ifp;
133 {
134 	unsigned socksize, ifasize;
135 	int namelen, masklen;
136 	register struct sockaddr_dl *sdl;
137 	register struct ifaddr *ifa;
138 	static size_t if_indexlim = 8;
139 
140 	if (if_index == 0)
141 		TAILQ_INIT(&ifnet);
142 	TAILQ_INIT(&ifp->if_addrlist);
143 	TAILQ_INSERT_TAIL(&ifnet, ifp, if_list);
144 	ifp->if_index = ++if_index;
145 
146 	/*
147 	 * We have some arrays that should be indexed by if_index.
148 	 * since if_index will grow dynamically, they should grow too.
149 	 *	struct ifadd **ifnet_addrs
150 	 *	struct ifnet **ifindex2ifnet
151 	 */
152 	if (ifnet_addrs == 0 || ifindex2ifnet == 0 || if_index >= if_indexlim) {
153 		size_t n;
154 		caddr_t q;
155 
156 		while (if_index >= if_indexlim)
157 			if_indexlim <<= 1;
158 
159 		/* grow ifnet_addrs */
160 		n = if_indexlim * sizeof(ifa);
161 		q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
162 		bzero(q, n);
163 		if (ifnet_addrs) {
164 			bcopy((caddr_t)ifnet_addrs, q, n/2);
165 			free((caddr_t)ifnet_addrs, M_IFADDR);
166 		}
167 		ifnet_addrs = (struct ifaddr **)q;
168 
169 		/* grow ifindex2ifnet */
170 		n = if_indexlim * sizeof(struct ifnet *);
171 		q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
172 		bzero(q, n);
173 		if (ifindex2ifnet) {
174 			bcopy((caddr_t)ifindex2ifnet, q, n/2);
175 			free((caddr_t)ifindex2ifnet, M_IFADDR);
176 		}
177 		ifindex2ifnet = (struct ifnet **)q;
178 	}
179 
180 	ifindex2ifnet[if_index] = ifp;
181 
182 	/*
183 	 * create a Link Level name for this device
184 	 */
185 	namelen = strlen(ifp->if_xname);
186 	masklen = offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
187 	socksize = masklen + ifp->if_addrlen;
188 #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
189 	if (socksize < sizeof(*sdl))
190 		socksize = sizeof(*sdl);
191 	socksize = ROUNDUP(socksize);
192 	ifasize = sizeof(*ifa) + 2 * socksize;
193 	ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK);
194 	bzero((caddr_t)ifa, ifasize);
195 	sdl = (struct sockaddr_dl *)(ifa + 1);
196 	sdl->sdl_len = socksize;
197 	sdl->sdl_family = AF_LINK;
198 	bcopy(ifp->if_xname, sdl->sdl_data, namelen);
199 	sdl->sdl_nlen = namelen;
200 	sdl->sdl_index = ifp->if_index;
201 	sdl->sdl_type = ifp->if_type;
202 	ifnet_addrs[if_index] = ifa;
203 	ifa->ifa_ifp = ifp;
204 	ifa->ifa_rtrequest = link_rtrequest;
205 	TAILQ_INSERT_HEAD(&ifp->if_addrlist, ifa, ifa_list);
206 	ifa->ifa_addr = (struct sockaddr *)sdl;
207 	ifp->if_sadl = sdl;
208 	sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
209 	ifa->ifa_netmask = (struct sockaddr *)sdl;
210 	sdl->sdl_len = masklen;
211 	while (namelen != 0)
212 		sdl->sdl_data[--namelen] = 0xff;
213 	if (ifp->if_snd.ifq_maxlen == 0)
214 	    ifp->if_snd.ifq_maxlen = ifqmaxlen;
215 	ifp->if_broadcastaddr = 0; /* reliably crash if used uninitialized */
216 }
217 /*
218  * Locate an interface based on a complete address.
219  */
220 /*ARGSUSED*/
221 struct ifaddr *
222 ifa_ifwithaddr(addr)
223 	register struct sockaddr *addr;
224 {
225 	register struct ifnet *ifp;
226 	register struct ifaddr *ifa;
227 
228 #define	equal(a1, a2) \
229   (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
230 	for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
231 	    for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next) {
232 		if (ifa->ifa_addr->sa_family != addr->sa_family)
233 			continue;
234 		if (equal(addr, ifa->ifa_addr))
235 			return (ifa);
236 		if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr &&
237 		    /* IP6 doesn't have broadcast */
238 		    ifa->ifa_broadaddr->sa_len != 0 &&
239 		    equal(ifa->ifa_broadaddr, addr))
240 			return (ifa);
241 	}
242 	return ((struct ifaddr *)0);
243 }
244 
245 /*
246  * Locate the point to point interface with a given destination address.
247  */
248 /*ARGSUSED*/
249 struct ifaddr *
250 ifa_ifwithdstaddr(addr)
251 	register struct sockaddr *addr;
252 {
253 	register struct ifnet *ifp;
254 	register struct ifaddr *ifa;
255 
256 	for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
257 	    if (ifp->if_flags & IFF_POINTOPOINT)
258 		for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next) {
259 			if (ifa->ifa_addr->sa_family != addr->sa_family ||
260 			    ifa->ifa_dstaddr == NULL)
261 				continue;
262 			if (equal(addr, ifa->ifa_dstaddr))
263 				return (ifa);
264 	}
265 	return ((struct ifaddr *)0);
266 }
267 
268 /*
269  * Find an interface on a specific network.  If many, choice
270  * is most specific found.
271  */
272 struct ifaddr *
273 ifa_ifwithnet(addr)
274 	struct sockaddr *addr;
275 {
276 	register struct ifnet *ifp;
277 	register struct ifaddr *ifa;
278 	struct ifaddr *ifa_maybe = 0;
279 	u_int af = addr->sa_family;
280 	char *addr_data = addr->sa_data, *cplim;
281 
282 	if (af == AF_LINK) {
283 	    register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
284 	    if (sdl->sdl_index && sdl->sdl_index <= if_index)
285 		return (ifnet_addrs[sdl->sdl_index]);
286 	}
287 #ifdef NETATALK
288 	if (af == AF_APPLETALK) {
289 		for (ifp = ifnet.tqh_first; ifp != 0;
290 		    ifp = ifp->if_list.tqe_next) {
291 			ifa = at_ifawithnet((struct sockaddr_at *)addr, ifp);
292 			if (ifa)
293 				return ifa;
294 		}
295 		return NULL;
296 	}
297 #endif
298 	for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
299 		for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next) {
300 			register char *cp, *cp2, *cp3;
301 
302 			if (ifa->ifa_addr->sa_family != af ||
303 			    ifa->ifa_netmask == 0)
304 				next: continue;
305 			cp = addr_data;
306 			cp2 = ifa->ifa_addr->sa_data;
307 			cp3 = ifa->ifa_netmask->sa_data;
308 			cplim = (char *)ifa->ifa_netmask +
309 				ifa->ifa_netmask->sa_len;
310 			while (cp3 < cplim)
311 				if ((*cp++ ^ *cp2++) & *cp3++)
312 				    /* want to continue for() loop */
313 					goto next;
314 			if (ifa_maybe == 0 ||
315 			    rn_refines((caddr_t)ifa->ifa_netmask,
316 			    (caddr_t)ifa_maybe->ifa_netmask))
317 				ifa_maybe = ifa;
318 		}
319 	return (ifa_maybe);
320 }
321 /*
322  * Find the interface of the addresss.
323  */
324 struct ifaddr *
325 ifa_ifwithladdr(addr)
326 	struct sockaddr *addr;
327 {
328 	struct ifaddr *ia;
329 
330 	if ((ia = ifa_ifwithaddr(addr)) || (ia = ifa_ifwithdstaddr(addr))
331 	    || (ia = ifa_ifwithnet(addr)))
332 		return (ia);
333 	return (NULL);
334 }
335 
336 /*
337  * Find an interface using a specific address family
338  */
339 struct ifaddr *
340 ifa_ifwithaf(af)
341 	register int af;
342 {
343 	register struct ifnet *ifp;
344 	register struct ifaddr *ifa;
345 
346 	for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
347 		for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next)
348 			if (ifa->ifa_addr->sa_family == af)
349 				return (ifa);
350 	return ((struct ifaddr *)0);
351 }
352 
353 /*
354  * Find an interface address specific to an interface best matching
355  * a given address.
356  */
357 struct ifaddr *
358 ifaof_ifpforaddr(addr, ifp)
359 	struct sockaddr *addr;
360 	register struct ifnet *ifp;
361 {
362 	register struct ifaddr *ifa;
363 	register char *cp, *cp2, *cp3;
364 	register char *cplim;
365 	struct ifaddr *ifa_maybe = 0;
366 	u_int af = addr->sa_family;
367 
368 	if (af >= AF_MAX)
369 		return (0);
370 	for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next) {
371 		if (ifa->ifa_addr->sa_family != af)
372 			continue;
373 		ifa_maybe = ifa;
374 		if (ifa->ifa_netmask == 0) {
375 			if (equal(addr, ifa->ifa_addr) ||
376 			    (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
377 				return (ifa);
378 			continue;
379 		}
380 		cp = addr->sa_data;
381 		cp2 = ifa->ifa_addr->sa_data;
382 		cp3 = ifa->ifa_netmask->sa_data;
383 		cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
384 		for (; cp3 < cplim; cp3++)
385 			if ((*cp++ ^ *cp2++) & *cp3)
386 				break;
387 		if (cp3 == cplim)
388 			return (ifa);
389 	}
390 	return (ifa_maybe);
391 }
392 
393 #include <net/route.h>
394 
395 /*
396  * Default action when installing a route with a Link Level gateway.
397  * Lookup an appropriate real ifa to point to.
398  * This should be moved to /sys/net/link.c eventually.
399  */
400 void
401 link_rtrequest(cmd, rt, sa)
402 	int cmd;
403 	register struct rtentry *rt;
404 	struct sockaddr *sa;
405 {
406 	register struct ifaddr *ifa;
407 	struct sockaddr *dst;
408 	struct ifnet *ifp;
409 
410 	if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) ||
411 	    ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0))
412 		return;
413 	if ((ifa = ifaof_ifpforaddr(dst, ifp)) != NULL) {
414 		IFAFREE(rt->rt_ifa);
415 		rt->rt_ifa = ifa;
416 		ifa->ifa_refcnt++;
417 		if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest)
418 			ifa->ifa_rtrequest(cmd, rt, sa);
419 	}
420 }
421 
422 /*
423  * Mark an interface down and notify protocols of
424  * the transition.
425  * NOTE: must be called at splsoftnet or equivalent.
426  */
427 void
428 if_down(ifp)
429 	register struct ifnet *ifp;
430 {
431 	register struct ifaddr *ifa;
432 
433 	ifp->if_flags &= ~IFF_UP;
434 	for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next)
435 		pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
436 	if_qflush(&ifp->if_snd);
437 	rt_ifmsg(ifp);
438 }
439 
440 /*
441  * Mark an interface up and notify protocols of
442  * the transition.
443  * NOTE: must be called at splsoftnet or equivalent.
444  */
445 void
446 if_up(ifp)
447 	register struct ifnet *ifp;
448 {
449 #ifdef notyet
450 	register struct ifaddr *ifa;
451 #endif
452 
453 	ifp->if_flags |= IFF_UP;
454 #ifdef notyet
455 	/* this has no effect on IP, and will kill all ISO connections XXX */
456 	for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
457 	     ifa = ifa->ifa_list.tqe_next)
458 		pfctlinput(PRC_IFUP, ifa->ifa_addr);
459 #endif
460 	rt_ifmsg(ifp);
461 #ifdef INET6
462 	in6_if_up(ifp);
463 #endif
464 }
465 
466 /*
467  * Flush an interface queue.
468  */
469 void
470 if_qflush(ifq)
471 	register struct ifqueue *ifq;
472 {
473 	register struct mbuf *m, *n;
474 
475 	n = ifq->ifq_head;
476 	while ((m = n) != NULL) {
477 		n = m->m_act;
478 		m_freem(m);
479 	}
480 	ifq->ifq_head = 0;
481 	ifq->ifq_tail = 0;
482 	ifq->ifq_len = 0;
483 }
484 
485 /*
486  * Handle interface watchdog timer routines.  Called
487  * from softclock, we decrement timers (if set) and
488  * call the appropriate interface routine on expiration.
489  */
490 void
491 if_slowtimo(arg)
492 	void *arg;
493 {
494 	register struct ifnet *ifp;
495 	int s = splimp();
496 
497 	for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next) {
498 		if (ifp->if_timer == 0 || --ifp->if_timer)
499 			continue;
500 		if (ifp->if_watchdog)
501 			(*ifp->if_watchdog)(ifp);
502 	}
503 	splx(s);
504 	timeout(if_slowtimo, NULL, hz / IFNET_SLOWHZ);
505 }
506 
507 /*
508  * Map interface name to
509  * interface structure pointer.
510  */
511 struct ifnet *
512 ifunit(name)
513 	const char *name;
514 {
515 	register struct ifnet *ifp;
516 
517 	for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
518 		if (strcmp(ifp->if_xname, name) == 0)
519 			return (ifp);
520 
521 	return (NULL);
522 }
523 
524 
525 /*
526  * Map interface name in a sockaddr_dl to
527  * interface structure pointer.
528  */
529 struct ifnet *
530 if_withname(sa)
531 	struct sockaddr *sa;
532 {
533 	char ifname[IFNAMSIZ+1];
534 	struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
535 
536 	if ( (sa->sa_family != AF_LINK) || (sdl->sdl_nlen == 0) ||
537 	     (sdl->sdl_nlen > IFNAMSIZ) )
538 		return NULL;
539 
540 	/*
541 	 * ifunit wants a null-terminated name.  It may not be null-terminated
542 	 * in the sockaddr.  We don't want to change the caller's sockaddr,
543 	 * and there might not be room to put the trailing null anyway, so we
544 	 * make a local copy that we know we can null terminate safely.
545 	 */
546 
547 	bcopy(sdl->sdl_data, ifname, sdl->sdl_nlen);
548 	ifname[sdl->sdl_nlen] = '\0';
549 	return ifunit(ifname);
550 }
551 
552 
553 /*
554  * Interface ioctls.
555  */
556 int
557 ifioctl(so, cmd, data, p)
558 	struct socket *so;
559 	u_long cmd;
560 	caddr_t data;
561 	struct proc *p;
562 {
563 	register struct ifnet *ifp;
564 	register struct ifreq *ifr;
565 	int error = 0;
566 	short oif_flags;
567 
568 	switch (cmd) {
569 
570 	case SIOCGIFCONF:
571 	case OSIOCGIFCONF:
572 		return (ifconf(cmd, data));
573 	}
574 	ifr = (struct ifreq *)data;
575 	ifp = ifunit(ifr->ifr_name);
576 	if (ifp == 0)
577 		return (ENXIO);
578 	oif_flags = ifp->if_flags;
579 	switch (cmd) {
580 
581 	case SIOCGIFFLAGS:
582 		ifr->ifr_flags = ifp->if_flags;
583 		break;
584 
585 	case SIOCGIFMETRIC:
586 		ifr->ifr_metric = ifp->if_metric;
587 		break;
588 
589 	case SIOCGIFMTU:
590 		ifr->ifr_mtu = ifp->if_mtu;
591 		break;
592 
593 	case SIOCSIFFLAGS:
594 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
595 			return (error);
596 		if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) {
597 			int s = splimp();
598 			if_down(ifp);
599 			splx(s);
600 		}
601 		if (ifr->ifr_flags & IFF_UP && (ifp->if_flags & IFF_UP) == 0) {
602 			int s = splimp();
603 			if_up(ifp);
604 			splx(s);
605 		}
606 		ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
607 			(ifr->ifr_flags &~ IFF_CANTCHANGE);
608 		if (ifp->if_ioctl)
609 			(void) (*ifp->if_ioctl)(ifp, cmd, data);
610 		break;
611 
612 	case SIOCSIFMETRIC:
613 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
614 			return (error);
615 		ifp->if_metric = ifr->ifr_metric;
616 		break;
617 
618 	case SIOCSIFMTU:
619 	{
620 		u_long oldmtu = ifp->if_mtu;
621 
622 		error = suser(p->p_ucred, &p->p_acflag);
623 		if (error)
624 			return (error);
625 		if (ifp->if_ioctl == NULL)
626 			return (EOPNOTSUPP);
627 		error = (*ifp->if_ioctl)(ifp, cmd, data);
628 
629 		/*
630 		 * If the link MTU changed, do network layer specific procedure.
631 		 */
632 		if (ifp->if_mtu != oldmtu) {
633 #ifdef INET6
634 			nd6_setmtu(ifp);
635 #endif
636 		}
637 		break;
638 	}
639 	case SIOCADDMULTI:
640 	case SIOCDELMULTI:
641 	case SIOCSIFMEDIA:
642 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
643 			return (error);
644 		/* FALLTHROUGH */
645 	case SIOCGIFMEDIA:
646 		if (ifp->if_ioctl == 0)
647 			return (EOPNOTSUPP);
648 		error = (*ifp->if_ioctl)(ifp, cmd, data);
649 		break;
650 
651 	case SIOCSDRVSPEC:
652 		/* XXX:  need to pass proc pointer through to driver... */
653 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
654 			return (error);
655 	/* FALLTHROUGH */
656 	default:
657 		if (so->so_proto == 0)
658 			return (EOPNOTSUPP);
659 #if !defined(COMPAT_43) && !defined(COMPAT_LINUX) && !defined(COMPAT_SVR4)
660 		error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
661 		    (struct mbuf *)cmd, (struct mbuf *)data,
662 		    (struct mbuf *)ifp, p));
663 #else
664 	    {
665 		int ocmd = cmd;
666 
667 		switch (cmd) {
668 
669 		case SIOCSIFADDR:
670 		case SIOCSIFDSTADDR:
671 		case SIOCSIFBRDADDR:
672 		case SIOCSIFNETMASK:
673 #if BYTE_ORDER != BIG_ENDIAN
674 			if (ifr->ifr_addr.sa_family == 0 &&
675 			    ifr->ifr_addr.sa_len < 16) {
676 				ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len;
677 				ifr->ifr_addr.sa_len = 16;
678 			}
679 #else
680 			if (ifr->ifr_addr.sa_len == 0)
681 				ifr->ifr_addr.sa_len = 16;
682 #endif
683 			break;
684 
685 		case OSIOCGIFADDR:
686 			cmd = SIOCGIFADDR;
687 			break;
688 
689 		case OSIOCGIFDSTADDR:
690 			cmd = SIOCGIFDSTADDR;
691 			break;
692 
693 		case OSIOCGIFBRDADDR:
694 			cmd = SIOCGIFBRDADDR;
695 			break;
696 
697 		case OSIOCGIFNETMASK:
698 			cmd = SIOCGIFNETMASK;
699 		}
700 
701 		error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
702 		    (struct mbuf *)cmd, (struct mbuf *)data,
703 		    (struct mbuf *)ifp, p));
704 
705 		switch (ocmd) {
706 		case OSIOCGIFADDR:
707 		case OSIOCGIFDSTADDR:
708 		case OSIOCGIFBRDADDR:
709 		case OSIOCGIFNETMASK:
710 			*(u_int16_t *)&ifr->ifr_addr = ifr->ifr_addr.sa_family;
711 		}
712 	    }
713 #endif /* COMPAT_43 */
714 		break;
715 	}
716 
717 	if (((oif_flags ^ ifp->if_flags) & IFF_UP) != 0) {
718 #ifdef INET6
719 		if ((ifp->if_flags & IFF_UP) != 0) {
720 			int s = splimp();
721 			in6_if_up(ifp);
722 			splx(s);
723 		}
724 #endif
725 	}
726 
727 	return (error);
728 }
729 
730 /*
731  * Return interface configuration
732  * of system.  List may be used
733  * in later ioctl's (above) to get
734  * other information.
735  */
736 /*ARGSUSED*/
737 int
738 ifconf(cmd, data)
739 	u_long cmd;
740 	caddr_t data;
741 {
742 	register struct ifconf *ifc = (struct ifconf *)data;
743 	register struct ifnet *ifp;
744 	register struct ifaddr *ifa;
745 	struct ifreq ifr, *ifrp;
746 	int space = ifc->ifc_len, error = 0;
747 
748 	ifrp = ifc->ifc_req;
749 	for (ifp = ifnet.tqh_first;
750 	    space >= sizeof (ifr) && ifp != 0; ifp = ifp->if_list.tqe_next) {
751 		bcopy(ifp->if_xname, ifr.ifr_name, IFNAMSIZ);
752 		if ((ifa = ifp->if_addrlist.tqh_first) == 0) {
753 			bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
754 			error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
755 			    sizeof(ifr));
756 			if (error)
757 				break;
758 			space -= sizeof (ifr), ifrp++;
759 		} else
760 		    for (; space >= sizeof (ifr) && ifa != 0; ifa = ifa->ifa_list.tqe_next) {
761 			register struct sockaddr *sa = ifa->ifa_addr;
762 #if defined(COMPAT_43) || defined(COMPAT_LINUX) || defined(COMPAT_SVR4)
763 			if (cmd == OSIOCGIFCONF) {
764 				struct osockaddr *osa =
765 					 (struct osockaddr *)&ifr.ifr_addr;
766 				ifr.ifr_addr = *sa;
767 				osa->sa_family = sa->sa_family;
768 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
769 						sizeof (ifr));
770 				ifrp++;
771 			} else
772 #endif
773 			if (sa->sa_len <= sizeof(*sa)) {
774 				ifr.ifr_addr = *sa;
775 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
776 						sizeof (ifr));
777 				ifrp++;
778 			} else {
779 				space -= sa->sa_len - sizeof(*sa);
780 				if (space < sizeof (ifr))
781 					break;
782 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
783 						sizeof (ifr.ifr_name));
784 				if (error == 0)
785 				    error = copyout((caddr_t)sa,
786 				      (caddr_t)&ifrp->ifr_addr, sa->sa_len);
787 				ifrp = (struct ifreq *)
788 					(sa->sa_len + (caddr_t)&ifrp->ifr_addr);
789 			}
790 			if (error)
791 				break;
792 			space -= sizeof (ifr);
793 		}
794 	}
795 	ifc->ifc_len -= space;
796 	return (error);
797 }
798