Edit

IABSD.fr/src/sys/net/if.c

Branch :

  • Show log

    Commit

  • Author : bluhm
    Date : 2024-01-06 11:42:11
    Hash : 79307011
    Message : Do not count packets though multicast loopback and simplex interfaces. Counting multicast packets sent to local stack or packets that are reflected by simplex interfaces does not make much sense. They are neither received nor output by any ethernet device. Counting these packets at lo0 or the loopback interface of the routing domain would be possible, but is not worth the effort. Make if_input_local() MP safe by deleting the if_opackets++ code. OK mvs@

  • sys/net/if.c
  • /*	$OpenBSD: if.c,v 1.716 2024/01/06 11:42:11 bluhm Exp $	*/
    /*	$NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $	*/
    
    /*
     * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. Neither the name of the project nor the names of its contributors
     *    may be used to endorse or promote products derived from this software
     *    without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     */
    
    /*
     * Copyright (c) 1980, 1986, 1993
     *	The Regents of the University of California.  All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. Neither the name of the University nor the names of its contributors
     *    may be used to endorse or promote products derived from this software
     *    without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     *
     *	@(#)if.c	8.3 (Berkeley) 1/4/94
     */
    
    #include "bpfilter.h"
    #include "bridge.h"
    #include "carp.h"
    #include "ether.h"
    #include "pf.h"
    #include "pfsync.h"
    #include "ppp.h"
    #include "pppoe.h"
    #include "if_wg.h"
    
    #include <sys/param.h>
    #include <sys/systm.h>
    #include <sys/mbuf.h>
    #include <sys/socket.h>
    #include <sys/socketvar.h>
    #include <sys/timeout.h>
    #include <sys/protosw.h>
    #include <sys/kernel.h>
    #include <sys/ioctl.h>
    #include <sys/domain.h>
    #include <sys/task.h>
    #include <sys/atomic.h>
    #include <sys/percpu.h>
    #include <sys/proc.h>
    #include <sys/stdint.h>	/* uintptr_t */
    #include <sys/rwlock.h>
    #include <sys/smr.h>
    
    #include <net/if.h>
    #include <net/if_dl.h>
    #include <net/if_types.h>
    #include <net/route.h>
    #include <net/netisr.h>
    
    #include "vlan.h"
    #if NVLAN > 0
    #include <net/if_vlan_var.h>
    #endif
    
    #include <netinet/in.h>
    #include <netinet/if_ether.h>
    #include <netinet/igmp.h>
    #ifdef MROUTING
    #include <netinet/ip_mroute.h>
    #endif
    #include <netinet/tcp.h>
    #include <netinet/tcp_timer.h>
    #include <netinet/tcp_var.h>
    
    #ifdef INET6
    #include <netinet6/in6_var.h>
    #include <netinet6/in6_ifattach.h>
    #include <netinet6/nd6.h>
    #include <netinet/ip6.h>
    #include <netinet6/ip6_var.h>
    #endif
    
    #ifdef MPLS
    #include <netmpls/mpls.h>
    #endif
    
    #if NBPFILTER > 0
    #include <net/bpf.h>
    #endif
    
    #if NBRIDGE > 0
    #include <net/if_bridge.h>
    #endif
    
    #if NCARP > 0
    #include <netinet/ip_carp.h>
    #endif
    
    #if NPF > 0
    #include <net/pfvar.h>
    #endif
    
    #include <sys/device.h>
    
    void	if_attachsetup(struct ifnet *);
    void	if_attach_common(struct ifnet *);
    void	if_remove(struct ifnet *);
    int	if_createrdomain(int, struct ifnet *);
    int	if_setrdomain(struct ifnet *, int);
    void	if_slowtimo(void *);
    
    void	if_detached_qstart(struct ifqueue *);
    int	if_detached_ioctl(struct ifnet *, u_long, caddr_t);
    
    int	ifioctl_get(u_long, caddr_t);
    int	ifconf(caddr_t);
    static int
    	if_sffpage_check(const caddr_t);
    
    int	if_getgroup(caddr_t, struct ifnet *);
    int	if_getgroupmembers(caddr_t);
    int	if_getgroupattribs(caddr_t);
    int	if_setgroupattribs(caddr_t);
    int	if_getgrouplist(caddr_t);
    
    void	if_linkstate(struct ifnet *);
    void	if_linkstate_task(void *);
    
    int	if_clone_list(struct if_clonereq *);
    struct if_clone	*if_clone_lookup(const char *, int *);
    
    int	if_group_egress_build(void);
    
    void	if_watchdog_task(void *);
    
    void	if_netisr(void *);
    
    #ifdef DDB
    void	ifa_print_all(void);
    #endif
    
    void	if_qstart_compat(struct ifqueue *);
    
    /*
     * interface index map
     *
     * the kernel maintains a mapping of interface indexes to struct ifnet
     * pointers.
     *
     * the map is an array of struct ifnet pointers prefixed by an if_map
     * structure. the if_map structure stores the length of its array.
     *
     * as interfaces are attached to the system, the map is grown on demand
     * up to USHRT_MAX entries.
     *
     * interface index 0 is reserved and represents no interface. this
     * supports the use of the interface index as the scope for IPv6 link
     * local addresses, where scope 0 means no scope has been specified.
     * it also supports the use of interface index as the unique identifier
     * for network interfaces in SNMP applications as per RFC2863. therefore
     * if_get(0) returns NULL.
     */
    
    struct ifnet *if_ref(struct ifnet *);
    
    /*
     * struct if_idxmap
     *
     * infrastructure to manage updates and accesses to the current if_map.
     *
     * interface index 0 is special and represents "no interface", so we
     * use the 0th slot in map to store the length of the array.
     */
    
    struct if_idxmap {
    	unsigned int		  serial;
    	unsigned int		  count;
    	struct ifnet		**map;		/* SMR protected */
    	struct rwlock		  lock;
    	unsigned char		 *usedidx;	/* bitmap of indices in use */
    };
    
    struct if_idxmap_dtor {
    	struct smr_entry	  smr;
    	struct ifnet		**map;
    };
    
    void	if_idxmap_init(unsigned int);
    void	if_idxmap_free(void *);
    void	if_idxmap_alloc(struct ifnet *);
    void	if_idxmap_insert(struct ifnet *);
    void	if_idxmap_remove(struct ifnet *);
    
    TAILQ_HEAD(, ifg_group) ifg_head =
        TAILQ_HEAD_INITIALIZER(ifg_head);	/* [N] list of interface groups */
    
    LIST_HEAD(, if_clone) if_cloners =
        LIST_HEAD_INITIALIZER(if_cloners);	/* [I] list of clonable interfaces */
    int if_cloners_count;	/* [I] number of clonable interfaces */
    
    struct rwlock if_cloners_lock = RWLOCK_INITIALIZER("clonelk");
    
    /* hooks should only be added, deleted, and run from a process context */
    struct mutex if_hooks_mtx = MUTEX_INITIALIZER(IPL_NONE);
    void	if_hooks_run(struct task_list *);
    
    int	ifq_congestion;
    
    int		 netisr;
    
    struct softnet {
    	char		 sn_name[16];
    	struct taskq	*sn_taskq;
    };
    
    #define	NET_TASKQ	4
    struct softnet	softnets[NET_TASKQ];
    
    struct task if_input_task_locked = TASK_INITIALIZER(if_netisr, NULL);
    
    /*
     * Serialize socket operations to ensure no new sleeping points
     * are introduced in IP output paths.
     */
    struct rwlock netlock = RWLOCK_INITIALIZER("netlock");
    
    /*
     * Network interface utility routines.
     */
    void
    ifinit(void)
    {
    	unsigned int	i;
    
    	/*
    	 * most machines boot with 4 or 5 interfaces, so size the initial map
    	 * to accommodate this
    	 */
    	if_idxmap_init(8); /* 8 is a nice power of 2 for malloc */
    
    	for (i = 0; i < NET_TASKQ; i++) {
    		struct softnet *sn = &softnets[i];
    		snprintf(sn->sn_name, sizeof(sn->sn_name), "softnet%u", i);
    		sn->sn_taskq = taskq_create(sn->sn_name, 1, IPL_NET,
    		    TASKQ_MPSAFE);
    		if (sn->sn_taskq == NULL)
    			panic("unable to create network taskq %d", i);
    	}
    }
    
    static struct if_idxmap if_idxmap;
    
    /*
     * XXXSMP: For `ifnetlist' modification both kernel and net locks
     * should be taken. For read-only access only one lock of them required.
     */
    struct ifnet_head ifnetlist = TAILQ_HEAD_INITIALIZER(ifnetlist);
    
    static inline unsigned int
    if_idxmap_limit(struct ifnet **if_map)
    {
    	return ((uintptr_t)if_map[0]);
    }
    
    static inline size_t
    if_idxmap_usedidx_size(unsigned int limit)
    {
    	return (max(howmany(limit, NBBY), sizeof(struct if_idxmap_dtor)));
    }
    
    void
    if_idxmap_init(unsigned int limit)
    {
    	struct ifnet **if_map;
    
    	rw_init(&if_idxmap.lock, "idxmaplk");
    	if_idxmap.serial = 1; /* skip ifidx 0 */
    
    	if_map = mallocarray(limit, sizeof(*if_map), M_IFADDR,
    	    M_WAITOK | M_ZERO);
    
    	if_map[0] = (struct ifnet *)(uintptr_t)limit;
    
    	if_idxmap.usedidx = malloc(if_idxmap_usedidx_size(limit),
    	    M_IFADDR, M_WAITOK | M_ZERO);
    	setbit(if_idxmap.usedidx, 0); /* blacklist ifidx 0 */
    
    	/* this is called early so there's nothing to race with */
    	SMR_PTR_SET_LOCKED(&if_idxmap.map, if_map);
    }
    
    void
    if_idxmap_alloc(struct ifnet *ifp)
    {
    	struct ifnet **if_map;
    	unsigned int limit;
    	unsigned int index, i;
    
    	refcnt_init(&ifp->if_refcnt);
    
    	rw_enter_write(&if_idxmap.lock);
    
    	if (++if_idxmap.count >= USHRT_MAX)
    		panic("too many interfaces");
    
    	if_map = SMR_PTR_GET_LOCKED(&if_idxmap.map);
    	limit = if_idxmap_limit(if_map);
    
    	index = if_idxmap.serial++ & USHRT_MAX;
    
    	if (index >= limit) {
    		struct if_idxmap_dtor *dtor;
    		struct ifnet **oif_map;
    		unsigned int olimit;
    		unsigned char *nusedidx;
    
    		oif_map = if_map;
    		olimit = limit;
    
    		limit = olimit * 2;
    		if_map = mallocarray(limit, sizeof(*if_map), M_IFADDR,
    		    M_WAITOK | M_ZERO);
    		if_map[0] = (struct ifnet *)(uintptr_t)limit;
    		
    		for (i = 1; i < olimit; i++) {
    			struct ifnet *oifp = SMR_PTR_GET_LOCKED(&oif_map[i]);
    			if (oifp == NULL)
    				continue;
    
    			/*
    			 * nif_map isn't visible yet, so don't need
    			 * SMR_PTR_SET_LOCKED and its membar.
    			 */
    			if_map[i] = if_ref(oifp);
    		}
    
    		nusedidx = malloc(if_idxmap_usedidx_size(limit),
    		    M_IFADDR, M_WAITOK | M_ZERO);
    		memcpy(nusedidx, if_idxmap.usedidx, howmany(olimit, NBBY));
    
    		/* use the old usedidx bitmap as an smr_entry for the if_map */
    		dtor = (struct if_idxmap_dtor *)if_idxmap.usedidx;
    		if_idxmap.usedidx = nusedidx;
    
    		SMR_PTR_SET_LOCKED(&if_idxmap.map, if_map);
    
    		dtor->map = oif_map;
    		smr_init(&dtor->smr);
    		smr_call(&dtor->smr, if_idxmap_free, dtor);
    	}
    
    	/* pick the next free index */
    	for (i = 0; i < USHRT_MAX; i++) {
    		if (index != 0 && isclr(if_idxmap.usedidx, index))
    			break;
    
    		index = if_idxmap.serial++ & USHRT_MAX;
    	}
    	KASSERT(index != 0 && index < limit);
    	KASSERT(isclr(if_idxmap.usedidx, index));
    
    	setbit(if_idxmap.usedidx, index);
    	ifp->if_index = index;
    
    	rw_exit_write(&if_idxmap.lock);
    }
    
    void
    if_idxmap_free(void *arg)
    {
    	struct if_idxmap_dtor *dtor = arg;
    	struct ifnet **oif_map = dtor->map;
    	unsigned int olimit = if_idxmap_limit(oif_map);
    	unsigned int i;
    
    	for (i = 1; i < olimit; i++)
    		if_put(oif_map[i]);
    
    	free(oif_map, M_IFADDR, olimit * sizeof(*oif_map));
    	free(dtor, M_IFADDR, if_idxmap_usedidx_size(olimit));
    }
    
    void
    if_idxmap_insert(struct ifnet *ifp)
    {
    	struct ifnet **if_map;
    	unsigned int index = ifp->if_index;
    
    	rw_enter_write(&if_idxmap.lock);
    
    	if_map = SMR_PTR_GET_LOCKED(&if_idxmap.map);
    
    	KASSERTMSG(index != 0 && index < if_idxmap_limit(if_map),
    	    "%s(%p) index %u vs limit %u", ifp->if_xname, ifp, index,
    	    if_idxmap_limit(if_map));
    	KASSERT(SMR_PTR_GET_LOCKED(&if_map[index]) == NULL);
    	KASSERT(isset(if_idxmap.usedidx, index));
    
    	/* commit */
    	SMR_PTR_SET_LOCKED(&if_map[index], if_ref(ifp));
    
    	rw_exit_write(&if_idxmap.lock);
    }
    
    void
    if_idxmap_remove(struct ifnet *ifp)
    {
    	struct ifnet **if_map;
    	unsigned int index = ifp->if_index;
    
    	rw_enter_write(&if_idxmap.lock);
    
    	if_map = SMR_PTR_GET_LOCKED(&if_idxmap.map);
    
    	KASSERT(index != 0 && index < if_idxmap_limit(if_map));
    	KASSERT(SMR_PTR_GET_LOCKED(&if_map[index]) == ifp);
    	KASSERT(isset(if_idxmap.usedidx, index));
    
    	SMR_PTR_SET_LOCKED(&if_map[index], NULL);
    
    	if_idxmap.count--;
    	clrbit(if_idxmap.usedidx, index);
    	/* end of if_idxmap modifications */
    
    	rw_exit_write(&if_idxmap.lock);
    
    	smr_barrier();
    	if_put(ifp);
    }
    
    /*
     * Attach an interface to the
     * list of "active" interfaces.
     */
    void
    if_attachsetup(struct ifnet *ifp)
    {
    	unsigned long ifidx;
    
    	NET_ASSERT_LOCKED();
    
    	if_addgroup(ifp, IFG_ALL);
    
    #ifdef INET6
    	nd6_ifattach(ifp);
    #endif
    
    #if NPF > 0
    	pfi_attach_ifnet(ifp);
    #endif
    
    	timeout_set(&ifp->if_slowtimo, if_slowtimo, ifp);
    	if_slowtimo(ifp);
    
    	if_idxmap_insert(ifp);
    	KASSERT(if_get(0) == NULL);
    
    	ifidx = ifp->if_index;
    
    	task_set(&ifp->if_watchdogtask, if_watchdog_task, (void *)ifidx);
    	task_set(&ifp->if_linkstatetask, if_linkstate_task, (void *)ifidx);
    
    	/* Announce the interface. */
    	rtm_ifannounce(ifp, IFAN_ARRIVAL);
    }
    
    /*
     * Allocate the link level name for the specified interface.  This
     * is an attachment helper.  It must be called after ifp->if_addrlen
     * is initialized, which may not be the case when if_attach() is
     * called.
     */
    void
    if_alloc_sadl(struct ifnet *ifp)
    {
    	unsigned int socksize;
    	int namelen, masklen;
    	struct sockaddr_dl *sdl;
    
    	/*
    	 * If the interface already has a link name, release it
    	 * now.  This is useful for interfaces that can change
    	 * link types, and thus switch link names often.
    	 */
    	if_free_sadl(ifp);
    
    	namelen = strlen(ifp->if_xname);
    	masklen = offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
    	socksize = masklen + ifp->if_addrlen;
    #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
    	if (socksize < sizeof(*sdl))
    		socksize = sizeof(*sdl);
    	socksize = ROUNDUP(socksize);
    	sdl = malloc(socksize, M_IFADDR, M_WAITOK|M_ZERO);
    	sdl->sdl_len = socksize;
    	sdl->sdl_family = AF_LINK;
    	bcopy(ifp->if_xname, sdl->sdl_data, namelen);
    	sdl->sdl_nlen = namelen;
    	sdl->sdl_alen = ifp->if_addrlen;
    	sdl->sdl_index = ifp->if_index;
    	sdl->sdl_type = ifp->if_type;
    	ifp->if_sadl = sdl;
    }
    
    /*
     * Free the link level name for the specified interface.  This is
     * a detach helper.  This is called from if_detach() or from
     * link layer type specific detach functions.
     */
    void
    if_free_sadl(struct ifnet *ifp)
    {
    	if (ifp->if_sadl == NULL)
    		return;
    
    	free(ifp->if_sadl, M_IFADDR, ifp->if_sadl->sdl_len);
    	ifp->if_sadl = NULL;
    }
    
    void
    if_attachhead(struct ifnet *ifp)
    {
    	if_attach_common(ifp);
    	NET_LOCK();
    	TAILQ_INSERT_HEAD(&ifnetlist, ifp, if_list);
    	if_attachsetup(ifp);
    	NET_UNLOCK();
    }
    
    void
    if_attach(struct ifnet *ifp)
    {
    	if_attach_common(ifp);
    	NET_LOCK();
    	TAILQ_INSERT_TAIL(&ifnetlist, ifp, if_list);
    	if_attachsetup(ifp);
    	NET_UNLOCK();
    }
    
    void
    if_attach_queues(struct ifnet *ifp, unsigned int nqs)
    {
    	struct ifqueue **map;
    	struct ifqueue *ifq;
    	int i;
    
    	KASSERT(ifp->if_ifqs == ifp->if_snd.ifq_ifqs);
    	KASSERT(nqs != 0);
    
    	map = mallocarray(sizeof(*map), nqs, M_DEVBUF, M_WAITOK);
    
    	ifp->if_snd.ifq_softc = NULL;
    	map[0] = &ifp->if_snd;
    
    	for (i = 1; i < nqs; i++) {
    		ifq = malloc(sizeof(*ifq), M_DEVBUF, M_WAITOK|M_ZERO);
    		ifq_init_maxlen(ifq, ifp->if_snd.ifq_maxlen);
    		ifq_init(ifq, ifp, i);
    		map[i] = ifq;
    	}
    
    	ifp->if_ifqs = map;
    	ifp->if_nifqs = nqs;
    }
    
    void
    if_attach_iqueues(struct ifnet *ifp, unsigned int niqs)
    {
    	struct ifiqueue **map;
    	struct ifiqueue *ifiq;
    	unsigned int i;
    
    	KASSERT(niqs != 0);
    
    	map = mallocarray(niqs, sizeof(*map), M_DEVBUF, M_WAITOK);
    
    	ifp->if_rcv.ifiq_softc = NULL;
    	map[0] = &ifp->if_rcv;
    
    	for (i = 1; i < niqs; i++) {
    		ifiq = malloc(sizeof(*ifiq), M_DEVBUF, M_WAITOK|M_ZERO);
    		ifiq_init(ifiq, ifp, i);
    		map[i] = ifiq;
    	}
    
    	ifp->if_iqs = map;
    	ifp->if_niqs = niqs;
    }
    
    void
    if_attach_common(struct ifnet *ifp)
    {
    	KASSERT(ifp->if_ioctl != NULL);
    
    	TAILQ_INIT(&ifp->if_addrlist);
    	TAILQ_INIT(&ifp->if_maddrlist);
    	TAILQ_INIT(&ifp->if_groups);
    
    	if (!ISSET(ifp->if_xflags, IFXF_MPSAFE)) {
    		KASSERTMSG(ifp->if_qstart == NULL,
    		    "%s: if_qstart set without MPSAFE set", ifp->if_xname);
    		ifp->if_qstart = if_qstart_compat;
    	} else {
    		KASSERTMSG(ifp->if_start == NULL,
    		    "%s: if_start set with MPSAFE set", ifp->if_xname);
    		KASSERTMSG(ifp->if_qstart != NULL,
    		    "%s: if_qstart not set with MPSAFE set", ifp->if_xname);
    	}
    
    	if_idxmap_alloc(ifp);
    
    	ifq_init(&ifp->if_snd, ifp, 0);
    
    	ifp->if_snd.ifq_ifqs[0] = &ifp->if_snd;
    	ifp->if_ifqs = ifp->if_snd.ifq_ifqs;
    	ifp->if_nifqs = 1;
    	if (ifp->if_txmit == 0)
    		ifp->if_txmit = IF_TXMIT_DEFAULT;
    
    	ifiq_init(&ifp->if_rcv, ifp, 0);
    
    	ifp->if_rcv.ifiq_ifiqs[0] = &ifp->if_rcv;
    	ifp->if_iqs = ifp->if_rcv.ifiq_ifiqs;
    	ifp->if_niqs = 1;
    
    	TAILQ_INIT(&ifp->if_addrhooks);
    	TAILQ_INIT(&ifp->if_linkstatehooks);
    	TAILQ_INIT(&ifp->if_detachhooks);
    
    	if (ifp->if_rtrequest == NULL)
    		ifp->if_rtrequest = if_rtrequest_dummy;
    	if (ifp->if_enqueue == NULL)
    		ifp->if_enqueue = if_enqueue_ifq;
    #if NBPFILTER > 0
    	if (ifp->if_bpf_mtap == NULL)
    		ifp->if_bpf_mtap = bpf_mtap_ether;
    #endif
    	ifp->if_llprio = IFQ_DEFPRIO;
    }
    
    void
    if_attach_ifq(struct ifnet *ifp, const struct ifq_ops *newops, void *args)
    {
    	/*
    	 * only switch the ifq_ops on the first ifq on an interface.
    	 *
    	 * the only ifq_ops we provide priq and hfsc, and hfsc only
    	 * works on a single ifq. because the code uses the ifq_ops
    	 * on the first ifq (if_snd) to select a queue for an mbuf,
    	 * by switching only the first one we change both the algorithm
    	 * and force the routing of all new packets to it.
    	 */
    	ifq_attach(&ifp->if_snd, newops, args);
    }
    
    void
    if_start(struct ifnet *ifp)
    {
    	KASSERT(ifp->if_qstart == if_qstart_compat);
    	if_qstart_compat(&ifp->if_snd);
    }
    void
    if_qstart_compat(struct ifqueue *ifq)
    {
    	struct ifnet *ifp = ifq->ifq_if;
    	int s;
    
    	/*
    	 * the stack assumes that an interface can have multiple
    	 * transmit rings, but a lot of drivers are still written
    	 * so that interfaces and send rings have a 1:1 mapping.
    	 * this provides compatibility between the stack and the older
    	 * drivers by translating from the only queue they have
    	 * (ifp->if_snd) back to the interface and calling if_start.
    	 */
    
    	KERNEL_LOCK();
    	s = splnet();
    	(*ifp->if_start)(ifp);
    	splx(s);
    	KERNEL_UNLOCK();
    }
    
    int
    if_enqueue(struct ifnet *ifp, struct mbuf *m)
    {
    	CLR(m->m_pkthdr.csum_flags, M_TIMESTAMP);
    
    #if NPF > 0
    	if (m->m_pkthdr.pf.delay > 0)
    		return (pf_delay_pkt(m, ifp->if_index));
    #endif
    
    #if NBRIDGE > 0
    	if (ifp->if_bridgeidx && (m->m_flags & M_PROTO1) == 0) {
    		int error;
    
    		error = bridge_enqueue(ifp, m);
    		return (error);
    	}
    #endif
    
    #if NPF > 0
    	pf_pkt_addr_changed(m);
    #endif	/* NPF > 0 */
    
    	return ((*ifp->if_enqueue)(ifp, m));
    }
    
    int
    if_enqueue_ifq(struct ifnet *ifp, struct mbuf *m)
    {
    	struct ifqueue *ifq = &ifp->if_snd;
    	int error;
    
    	if (ifp->if_nifqs > 1) {
    		unsigned int idx;
    
    		/*
    		 * use the operations on the first ifq to pick which of
    		 * the array gets this mbuf.
    		 */
    
    		idx = ifq_idx(&ifp->if_snd, ifp->if_nifqs, m);
    		ifq = ifp->if_ifqs[idx];
    	}
    
    	error = ifq_enqueue(ifq, m);
    	if (error)
    		return (error);
    
    	ifq_start(ifq);
    
    	return (0);
    }
    
    void
    if_input(struct ifnet *ifp, struct mbuf_list *ml)
    {
    	ifiq_input(&ifp->if_rcv, ml);
    }
    
    int
    if_input_local(struct ifnet *ifp, struct mbuf *m, sa_family_t af)
    {
    	int keepflags, keepcksum;
    	uint16_t keepmss;
    
    #if NBPFILTER > 0
    	/*
    	 * Only send packets to bpf if they are destined to local
    	 * addresses.
    	 *
    	 * if_input_local() is also called for SIMPLEX interfaces to
    	 * duplicate packets for local use.  But don't dup them to bpf.
    	 */
    	if (ifp->if_flags & IFF_LOOPBACK) {
    		caddr_t if_bpf = ifp->if_bpf;
    
    		if (if_bpf)
    			bpf_mtap_af(if_bpf, af, m, BPF_DIRECTION_OUT);
    	}
    #endif
    	keepflags = m->m_flags & (M_BCAST|M_MCAST);
    	/*
    	 * Preserve outgoing checksum flags, in case the packet is
    	 * forwarded to another interface.  Then the checksum, which
    	 * is now incorrect, will be calculated before sending.
    	 */
    	keepcksum = m->m_pkthdr.csum_flags & (M_IPV4_CSUM_OUT |
    	    M_TCP_CSUM_OUT | M_UDP_CSUM_OUT | M_ICMP_CSUM_OUT |
    	    M_TCP_TSO);
    	keepmss = m->m_pkthdr.ph_mss;
    	m_resethdr(m);
    	m->m_flags |= M_LOOP | keepflags;
    	m->m_pkthdr.csum_flags = keepcksum;
    	m->m_pkthdr.ph_mss = keepmss;
    	m->m_pkthdr.ph_ifidx = ifp->if_index;
    	m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
    
    	if (ISSET(keepcksum, M_TCP_TSO) && m->m_pkthdr.len > ifp->if_mtu) {
    		if (ifp->if_mtu > 0 &&
    		    ((af == AF_INET &&
    		    ISSET(ifp->if_capabilities, IFCAP_TSOv4)) ||
    		    (af == AF_INET6 &&
    		    ISSET(ifp->if_capabilities, IFCAP_TSOv6)))) {
    			tcpstat_inc(tcps_inswlro);
    			tcpstat_add(tcps_inpktlro,
    			    (m->m_pkthdr.len + ifp->if_mtu - 1) / ifp->if_mtu);
    		} else {
    			tcpstat_inc(tcps_inbadlro);
    			m_freem(m);
    			return (EPROTONOSUPPORT);
    		}
    	}
    
    	if (ISSET(keepcksum, M_TCP_CSUM_OUT))
    		m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK;
    	if (ISSET(keepcksum, M_UDP_CSUM_OUT))
    		m->m_pkthdr.csum_flags |= M_UDP_CSUM_IN_OK;
    	if (ISSET(keepcksum, M_ICMP_CSUM_OUT))
    		m->m_pkthdr.csum_flags |= M_ICMP_CSUM_IN_OK;
    
    	/* do not count multicast loopback and simplex interfaces */
    	if (ISSET(ifp->if_flags, IFF_LOOPBACK)) {
    		counters_pkt(ifp->if_counters, ifc_opackets, ifc_obytes,
    		    m->m_pkthdr.len);
    	}
    
    	switch (af) {
    	case AF_INET:
    		if (ISSET(keepcksum, M_IPV4_CSUM_OUT))
    			m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
    		ipv4_input(ifp, m);
    		break;
    #ifdef INET6
    	case AF_INET6:
    		ipv6_input(ifp, m);
    		break;
    #endif /* INET6 */
    #ifdef MPLS
    	case AF_MPLS:
    		mpls_input(ifp, m);
    		break;
    #endif /* MPLS */
    	default:
    		printf("%s: can't handle af%d\n", ifp->if_xname, af);
    		m_freem(m);
    		return (EAFNOSUPPORT);
    	}
    
    	return (0);
    }
    
    int
    if_output_ml(struct ifnet *ifp, struct mbuf_list *ml,
        struct sockaddr *dst, struct rtentry *rt)
    {
    	struct mbuf *m;
    	int error = 0;
    
    	while ((m = ml_dequeue(ml)) != NULL) {
    		error = ifp->if_output(ifp, m, dst, rt);
    		if (error)
    			break;
    	}
    	if (error)
    		ml_purge(ml);
    
    	return error;
    }
    
    int
    if_output_tso(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst,
        struct rtentry *rt, u_int mtu)
    {
    	uint32_t ifcap;
    	int error;
    
    	switch (dst->sa_family) {
    	case AF_INET:
    		ifcap = IFCAP_TSOv4;
    		break;
    #ifdef INET6
    	case AF_INET6:
    		ifcap = IFCAP_TSOv6;
    		break;
    #endif
    	default:
    		unhandled_af(dst->sa_family);
    	}
    
    	/*
    	 * Try to send with TSO first.  When forwarding LRO may set
    	 * maximum segment size in mbuf header.  Chop TCP segment
    	 * even if it would fit interface MTU to preserve maximum
    	 * path MTU.
    	 */
    	error = tcp_if_output_tso(ifp, mp, dst, rt, ifcap, mtu);
    	if (error || *mp == NULL)
    		return error;
    
    	if ((*mp)->m_pkthdr.len <= mtu) {
    		switch (dst->sa_family) {
    		case AF_INET:
    			in_hdr_cksum_out(*mp, ifp);
    			in_proto_cksum_out(*mp, ifp);
    			break;
    #ifdef INET6
    		case AF_INET6:
    			in6_proto_cksum_out(*mp, ifp);
    			break;
    #endif
    		}
    		error = ifp->if_output(ifp, *mp, dst, rt);
    		*mp = NULL;
    		return error;
    	}
    
    	/* mp still contains mbuf that has to be fragmented or dropped. */
    	return 0;
    }
    
    int
    if_output_mq(struct ifnet *ifp, struct mbuf_queue *mq, unsigned int *total,
        struct sockaddr *dst, struct rtentry *rt)
    {
    	struct mbuf_list ml;
    	unsigned int len;
    	int error;
    
    	mq_delist(mq, &ml);
    	len = ml_len(&ml);
    	error = if_output_ml(ifp, &ml, dst, rt);
    
    	/* XXXSMP we also discard if other CPU enqueues */
    	if (mq_len(mq) > 0) {
    		/* mbuf is back in queue. Discard. */
    		atomic_sub_int(total, len + mq_purge(mq));
    	} else
    		atomic_sub_int(total, len);
    
    	return error;
    }
    
    int
    if_output_local(struct ifnet *ifp, struct mbuf *m, sa_family_t af)
    {
    	struct ifiqueue *ifiq;
    	unsigned int flow = 0;
    
    	m->m_pkthdr.ph_family = af;
    	m->m_pkthdr.ph_ifidx = ifp->if_index;
    	m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
    
    	if (ISSET(m->m_pkthdr.csum_flags, M_FLOWID))
    		flow = m->m_pkthdr.ph_flowid;
    
    	ifiq = ifp->if_iqs[flow % ifp->if_niqs];
    
    	return (ifiq_enqueue(ifiq, m) == 0 ? 0 : ENOBUFS);
    }
    
    void
    if_input_process(struct ifnet *ifp, struct mbuf_list *ml)
    {
    	struct mbuf *m;
    
    	if (ml_empty(ml))
    		return;
    
    	if (!ISSET(ifp->if_xflags, IFXF_CLONED))
    		enqueue_randomness(ml_len(ml) ^ (uintptr_t)MBUF_LIST_FIRST(ml));
    
    	/*
    	 * We grab the shared netlock for packet processing in the softnet
    	 * threads.  Packets can regrab the exclusive lock via queues.
    	 * ioctl, sysctl, and socket syscall may use shared lock if access is
    	 * read only or MP safe.  Usually they hold the exclusive net lock.
    	 */
    
    	NET_LOCK_SHARED();
    	while ((m = ml_dequeue(ml)) != NULL)
    		(*ifp->if_input)(ifp, m);
    	NET_UNLOCK_SHARED();
    }
    
    void
    if_vinput(struct ifnet *ifp, struct mbuf *m)
    {
    #if NBPFILTER > 0
    	caddr_t if_bpf;
    #endif
    
    	m->m_pkthdr.ph_ifidx = ifp->if_index;
    	m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
    
    	counters_pkt(ifp->if_counters,
    	    ifc_ipackets, ifc_ibytes, m->m_pkthdr.len);
    
    #if NPF > 0
    	pf_pkt_addr_changed(m);
    #endif
    
    #if NBPFILTER > 0
    	if_bpf = ifp->if_bpf;
    	if (if_bpf) {
    		if ((*ifp->if_bpf_mtap)(if_bpf, m, BPF_DIRECTION_IN)) {
    			m_freem(m);
    			return;
    		}
    	}
    #endif
    
    	if (__predict_true(!ISSET(ifp->if_xflags, IFXF_MONITOR)))
    		(*ifp->if_input)(ifp, m);
    	else
    		m_freem(m);
    }
    
    void
    if_netisr(void *unused)
    {
    	int n, t = 0;
    
    	NET_LOCK();
    
    	while ((n = netisr) != 0) {
    		/* Like sched_pause() but with a rwlock dance. */
    		if (curcpu()->ci_schedstate.spc_schedflags & SPCF_SHOULDYIELD) {
    			NET_UNLOCK();
    			yield();
    			NET_LOCK();
    		}
    
    		atomic_clearbits_int(&netisr, n);
    
    #if NETHER > 0
    		if (n & (1 << NETISR_ARP))
    			arpintr();
    #endif
    		if (n & (1 << NETISR_IP))
    			ipintr();
    #ifdef INET6
    		if (n & (1 << NETISR_IPV6))
    			ip6intr();
    #endif
    #if NPPP > 0
    		if (n & (1 << NETISR_PPP)) {
    			KERNEL_LOCK();
    			pppintr();
    			KERNEL_UNLOCK();
    		}
    #endif
    #if NBRIDGE > 0
    		if (n & (1 << NETISR_BRIDGE))
    			bridgeintr();
    #endif
    #ifdef PIPEX
    		if (n & (1 << NETISR_PIPEX))
    			pipexintr();
    #endif
    #if NPPPOE > 0
    		if (n & (1 << NETISR_PPPOE)) {
    			KERNEL_LOCK();
    			pppoeintr();
    			KERNEL_UNLOCK();
    		}
    #endif
    		t |= n;
    	}
    
    	NET_UNLOCK();
    }
    
    void
    if_hooks_run(struct task_list *hooks)
    {
    	struct task *t, *nt;
    	struct task cursor = { .t_func = NULL };
    	void (*func)(void *);
    	void *arg;
    
    	mtx_enter(&if_hooks_mtx);
    	for (t = TAILQ_FIRST(hooks); t != NULL; t = nt) {
    		if (t->t_func == NULL) { /* skip cursors */
    			nt = TAILQ_NEXT(t, t_entry);
    			continue;
    		}
    		func = t->t_func;
    		arg = t->t_arg;
    
    		TAILQ_INSERT_AFTER(hooks, t, &cursor, t_entry);
    		mtx_leave(&if_hooks_mtx);
    
    		(*func)(arg);
    
    		mtx_enter(&if_hooks_mtx);
    		nt = TAILQ_NEXT(&cursor, t_entry); /* avoid _Q_INVALIDATE */
    		TAILQ_REMOVE(hooks, &cursor, t_entry);
    	}
    	mtx_leave(&if_hooks_mtx);
    }
    
    void
    if_remove(struct ifnet *ifp)
    {
    	/* Remove the interface from the list of all interfaces. */
    	NET_LOCK();
    	TAILQ_REMOVE(&ifnetlist, ifp, if_list);
    	NET_UNLOCK();
    
    	/* Remove the interface from the interface index map. */
    	if_idxmap_remove(ifp);
    
    	/* Sleep until the last reference is released. */
    	refcnt_finalize(&ifp->if_refcnt, "ifrm");
    }
    
    void
    if_deactivate(struct ifnet *ifp)
    {
    	/*
    	 * Call detach hooks from head to tail.  To make sure detach
    	 * hooks are executed in the reverse order they were added, all
    	 * the hooks have to be added to the head!
    	 */
    
    	NET_LOCK();
    	if_hooks_run(&ifp->if_detachhooks);
    	NET_UNLOCK();
    }
    
    void
    if_detachhook_add(struct ifnet *ifp, struct task *t)
    {
    	mtx_enter(&if_hooks_mtx);
    	TAILQ_INSERT_HEAD(&ifp->if_detachhooks, t, t_entry);
    	mtx_leave(&if_hooks_mtx);
    }
    
    void
    if_detachhook_del(struct ifnet *ifp, struct task *t)
    {
    	mtx_enter(&if_hooks_mtx);
    	TAILQ_REMOVE(&ifp->if_detachhooks, t, t_entry);
    	mtx_leave(&if_hooks_mtx);
    }
    
    /*
     * Detach an interface from everything in the kernel.  Also deallocate
     * private resources.
     */
    void
    if_detach(struct ifnet *ifp)
    {
    	struct ifaddr *ifa;
    	struct ifg_list *ifg;
    	int i, s;
    
    	/* Undo pseudo-driver changes. */
    	if_deactivate(ifp);
    
    	/* Other CPUs must not have a reference before we start destroying. */
    	if_remove(ifp);
    
    	ifp->if_qstart = if_detached_qstart;
    
    	/* Wait until the start routines finished. */
    	ifq_barrier(&ifp->if_snd);
    	ifq_clr_oactive(&ifp->if_snd);
    
    #if NBPFILTER > 0
    	bpfdetach(ifp);
    #endif
    
    	NET_LOCK();
    	s = splnet();
    	ifp->if_ioctl = if_detached_ioctl;
    	ifp->if_watchdog = NULL;
    
    	/* Remove the watchdog timeout & task */
    	timeout_del(&ifp->if_slowtimo);
    	task_del(net_tq(ifp->if_index), &ifp->if_watchdogtask);
    
    	/* Remove the link state task */
    	task_del(net_tq(ifp->if_index), &ifp->if_linkstatetask);
    
    	rti_delete(ifp);
    #if NETHER > 0 && defined(NFSCLIENT)
    	if (ifp->if_index == revarp_ifidx)
    		revarp_ifidx = 0;
    #endif
    #ifdef MROUTING
    	vif_delete(ifp);
    #endif
    	in_ifdetach(ifp);
    #ifdef INET6
    	in6_ifdetach(ifp);
    #endif
    #if NPF > 0
    	pfi_detach_ifnet(ifp);
    #endif
    
    	while ((ifg = TAILQ_FIRST(&ifp->if_groups)) != NULL)
    		if_delgroup(ifp, ifg->ifgl_group->ifg_group);
    
    	if_free_sadl(ifp);
    
    	/* We should not have any address left at this point. */
    	if (!TAILQ_EMPTY(&ifp->if_addrlist)) {
    #ifdef DIAGNOSTIC
    		printf("%s: address list non empty\n", ifp->if_xname);
    #endif
    		while ((ifa = TAILQ_FIRST(&ifp->if_addrlist)) != NULL) {
    			ifa_del(ifp, ifa);
    			ifa->ifa_ifp = NULL;
    			ifafree(ifa);
    		}
    	}
    	splx(s);
    	NET_UNLOCK();
    
    	KASSERT(TAILQ_EMPTY(&ifp->if_addrhooks));
    	KASSERT(TAILQ_EMPTY(&ifp->if_linkstatehooks));
    	KASSERT(TAILQ_EMPTY(&ifp->if_detachhooks));
    
    #ifdef INET6
    	nd6_ifdetach(ifp);
    #endif
    
    	/* Announce that the interface is gone. */
    	rtm_ifannounce(ifp, IFAN_DEPARTURE);
    
    	if (ifp->if_counters != NULL)
    		if_counters_free(ifp);
    
    	for (i = 0; i < ifp->if_nifqs; i++)
    		ifq_destroy(ifp->if_ifqs[i]);
    	if (ifp->if_ifqs != ifp->if_snd.ifq_ifqs) {
    		for (i = 1; i < ifp->if_nifqs; i++) {
    			free(ifp->if_ifqs[i], M_DEVBUF,
    			    sizeof(struct ifqueue));
    		}
    		free(ifp->if_ifqs, M_DEVBUF,
    		    sizeof(struct ifqueue *) * ifp->if_nifqs);
    	}
    
    	for (i = 0; i < ifp->if_niqs; i++)
    		ifiq_destroy(ifp->if_iqs[i]);
    	if (ifp->if_iqs != ifp->if_rcv.ifiq_ifiqs) {
    		for (i = 1; i < ifp->if_niqs; i++) {
    			free(ifp->if_iqs[i], M_DEVBUF,
    			    sizeof(struct ifiqueue));
    		}
    		free(ifp->if_iqs, M_DEVBUF,
    		    sizeof(struct ifiqueue *) * ifp->if_niqs);
    	}
    }
    
    /*
     * Returns true if ``ifp0'' is connected to the interface with index ``ifidx''.
     */
    int
    if_isconnected(const struct ifnet *ifp0, unsigned int ifidx)
    {
    	struct ifnet *ifp;
    	int connected = 0;
    
    	ifp = if_get(ifidx);
    	if (ifp == NULL)
    		return (0);
    
    	if (ifp0->if_index == ifp->if_index)
    		connected = 1;
    
    #if NBRIDGE > 0
    	if (ifp0->if_bridgeidx != 0 && ifp0->if_bridgeidx == ifp->if_bridgeidx)
    		connected = 1;
    #endif
    #if NCARP > 0
    	if ((ifp0->if_type == IFT_CARP &&
    	    ifp0->if_carpdevidx == ifp->if_index) ||
    	    (ifp->if_type == IFT_CARP && ifp->if_carpdevidx == ifp0->if_index))
    		connected = 1;
    #endif
    
    	if_put(ifp);
    	return (connected);
    }
    
    /*
     * Create a clone network interface.
     */
    int
    if_clone_create(const char *name, int rdomain)
    {
    	struct if_clone *ifc;
    	struct ifnet *ifp;
    	int unit, ret;
    
    	ifc = if_clone_lookup(name, &unit);
    	if (ifc == NULL)
    		return (EINVAL);
    
    	rw_enter_write(&if_cloners_lock);
    
    	if ((ifp = if_unit(name)) != NULL) {
    		ret = EEXIST;
    		goto unlock;
    	}
    
    	ret = (*ifc->ifc_create)(ifc, unit);
    
    	if (ret != 0 || (ifp = if_unit(name)) == NULL)
    		goto unlock;
    
    	NET_LOCK();
    	if_addgroup(ifp, ifc->ifc_name);
    	if (rdomain != 0)
    		if_setrdomain(ifp, rdomain);
    	NET_UNLOCK();
    unlock:
    	rw_exit_write(&if_cloners_lock);
    	if_put(ifp);
    
    	return (ret);
    }
    
    /*
     * Destroy a clone network interface.
     */
    int
    if_clone_destroy(const char *name)
    {
    	struct if_clone *ifc;
    	struct ifnet *ifp;
    	int ret;
    
    	ifc = if_clone_lookup(name, NULL);
    	if (ifc == NULL)
    		return (EINVAL);
    
    	if (ifc->ifc_destroy == NULL)
    		return (EOPNOTSUPP);
    
    	rw_enter_write(&if_cloners_lock);
    
    	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
    		if (strcmp(ifp->if_xname, name) == 0)
    			break;
    	}
    	if (ifp == NULL) {
    		rw_exit_write(&if_cloners_lock);
    		return (ENXIO);
    	}
    
    	NET_LOCK();
    	if (ifp->if_flags & IFF_UP) {
    		int s;
    		s = splnet();
    		if_down(ifp);
    		splx(s);
    	}
    	NET_UNLOCK();
    	ret = (*ifc->ifc_destroy)(ifp);
    
    	rw_exit_write(&if_cloners_lock);
    
    	return (ret);
    }
    
    /*
     * Look up a network interface cloner.
     */
    struct if_clone *
    if_clone_lookup(const char *name, int *unitp)
    {
    	struct if_clone *ifc;
    	const char *cp;
    	int unit;
    
    	/* separate interface name from unit */
    	for (cp = name;
    	    cp - name < IFNAMSIZ && *cp && (*cp < '0' || *cp > '9');
    	    cp++)
    		continue;
    
    	if (cp == name || cp - name == IFNAMSIZ || !*cp)
    		return (NULL);	/* No name or unit number */
    
    	if (cp - name < IFNAMSIZ-1 && *cp == '0' && cp[1] != '\0')
    		return (NULL);	/* unit number 0 padded */
    
    	LIST_FOREACH(ifc, &if_cloners, ifc_list) {
    		if (strlen(ifc->ifc_name) == cp - name &&
    		    !strncmp(name, ifc->ifc_name, cp - name))
    			break;
    	}
    
    	if (ifc == NULL)
    		return (NULL);
    
    	unit = 0;
    	while (cp - name < IFNAMSIZ && *cp) {
    		if (*cp < '0' || *cp > '9' ||
    		    unit > (INT_MAX - (*cp - '0')) / 10) {
    			/* Bogus unit number. */
    			return (NULL);
    		}
    		unit = (unit * 10) + (*cp++ - '0');
    	}
    
    	if (unitp != NULL)
    		*unitp = unit;
    	return (ifc);
    }
    
    /*
     * Register a network interface cloner.
     */
    void
    if_clone_attach(struct if_clone *ifc)
    {
    	/*
    	 * we are called at kernel boot by main(), when pseudo devices are
    	 * being attached. The main() is the only guy which may alter the
    	 * if_cloners. While system is running and main() is done with
    	 * initialization, the if_cloners becomes immutable.
    	 */
    	KASSERT(pdevinit_done == 0);
    	LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
    	if_cloners_count++;
    }
    
    /*
     * Provide list of interface cloners to userspace.
     */
    int
    if_clone_list(struct if_clonereq *ifcr)
    {
    	char outbuf[IFNAMSIZ], *dst;
    	struct if_clone *ifc;
    	int count, error = 0;
    
    	if ((dst = ifcr->ifcr_buffer) == NULL) {
    		/* Just asking how many there are. */
    		ifcr->ifcr_total = if_cloners_count;
    		return (0);
    	}
    
    	if (ifcr->ifcr_count < 0)
    		return (EINVAL);
    
    	ifcr->ifcr_total = if_cloners_count;
    	count = MIN(if_cloners_count, ifcr->ifcr_count);
    
    	LIST_FOREACH(ifc, &if_cloners, ifc_list) {
    		if (count == 0)
    			break;
    		bzero(outbuf, sizeof outbuf);
    		strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
    		error = copyout(outbuf, dst, IFNAMSIZ);
    		if (error)
    			break;
    		count--;
    		dst += IFNAMSIZ;
    	}
    
    	return (error);
    }
    
    /*
     * set queue congestion marker
     */
    void
    if_congestion(void)
    {
    	extern int ticks;
    
    	ifq_congestion = ticks;
    }
    
    int
    if_congested(void)
    {
    	extern int ticks;
    	int diff;
    
    	diff = ticks - ifq_congestion;
    	if (diff < 0) {
    		ifq_congestion = ticks - hz;
    		return (0);
    	}
    
    	return (diff <= (hz / 100));
    }
    
    #define	equal(a1, a2)	\
    	(bcmp((caddr_t)(a1), (caddr_t)(a2),	\
    	(a1)->sa_len) == 0)
    
    /*
     * Locate an interface based on a complete address.
     */
    struct ifaddr *
    ifa_ifwithaddr(const struct sockaddr *addr, u_int rtableid)
    {
    	struct ifnet *ifp;
    	struct ifaddr *ifa;
    	u_int rdomain;
    
    	NET_ASSERT_LOCKED();
    
    	rdomain = rtable_l2(rtableid);
    	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
    		if (ifp->if_rdomain != rdomain)
    			continue;
    
    		TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
    			if (ifa->ifa_addr->sa_family != addr->sa_family)
    				continue;
    
    			if (equal(addr, ifa->ifa_addr)) {
    				return (ifa);
    			}
    		}
    	}
    	return (NULL);
    }
    
    /*
     * Locate the point to point interface with a given destination address.
     */
    struct ifaddr *
    ifa_ifwithdstaddr(const struct sockaddr *addr, u_int rdomain)
    {
    	struct ifnet *ifp;
    	struct ifaddr *ifa;
    
    	NET_ASSERT_LOCKED();
    
    	rdomain = rtable_l2(rdomain);
    	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
    		if (ifp->if_rdomain != rdomain)
    			continue;
    		if (ifp->if_flags & IFF_POINTOPOINT) {
    			TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
    				if (ifa->ifa_addr->sa_family !=
    				    addr->sa_family || ifa->ifa_dstaddr == NULL)
    					continue;
    				if (equal(addr, ifa->ifa_dstaddr)) {
    					return (ifa);
    				}
    			}
    		}
    	}
    	return (NULL);
    }
    
    /*
     * Find an interface address specific to an interface best matching
     * a given address.
     */
    struct ifaddr *
    ifaof_ifpforaddr(const struct sockaddr *addr, struct ifnet *ifp)
    {
    	struct ifaddr *ifa;
    	const char *cp, *cp2, *cp3;
    	char *cplim;
    	struct ifaddr *ifa_maybe = NULL;
    	u_int af = addr->sa_family;
    
    	if (af >= AF_MAX)
    		return (NULL);
    	TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
    		if (ifa->ifa_addr->sa_family != af)
    			continue;
    		if (ifa_maybe == NULL)
    			ifa_maybe = ifa;
    		if (ifa->ifa_netmask == 0 || ifp->if_flags & IFF_POINTOPOINT) {
    			if (equal(addr, ifa->ifa_addr) ||
    			    (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
    				return (ifa);
    			continue;
    		}
    		cp = addr->sa_data;
    		cp2 = ifa->ifa_addr->sa_data;
    		cp3 = ifa->ifa_netmask->sa_data;
    		cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
    		for (; cp3 < cplim; cp3++)
    			if ((*cp++ ^ *cp2++) & *cp3)
    				break;
    		if (cp3 == cplim)
    			return (ifa);
    	}
    	return (ifa_maybe);
    }
    
    void
    if_rtrequest_dummy(struct ifnet *ifp, int req, struct rtentry *rt)
    {
    }
    
    /*
     * Default action when installing a local route on a point-to-point
     * interface.
     */
    void
    p2p_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt)
    {
    	struct ifnet *lo0ifp;
    	struct ifaddr *ifa, *lo0ifa;
    
    	switch (req) {
    	case RTM_ADD:
    		if (!ISSET(rt->rt_flags, RTF_LOCAL))
    			break;
    
    		TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
    			if (memcmp(rt_key(rt), ifa->ifa_addr,
    			    rt_key(rt)->sa_len) == 0)
    				break;
    		}
    
    		if (ifa == NULL)
    			break;
    
    		KASSERT(ifa == rt->rt_ifa);
    
    		lo0ifp = if_get(rtable_loindex(ifp->if_rdomain));
    		KASSERT(lo0ifp != NULL);
    		TAILQ_FOREACH(lo0ifa, &lo0ifp->if_addrlist, ifa_list) {
    			if (lo0ifa->ifa_addr->sa_family ==
    			    ifa->ifa_addr->sa_family)
    				break;
    		}
    		if_put(lo0ifp);
    
    		if (lo0ifa == NULL)
    			break;
    
    		rt->rt_flags &= ~RTF_LLINFO;
    		break;
    	case RTM_DELETE:
    	case RTM_RESOLVE:
    	default:
    		break;
    	}
    }
    
    int
    p2p_bpf_mtap(caddr_t if_bpf, const struct mbuf *m, u_int dir)
    {
    #if NBPFILTER > 0
    	return (bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family, m, dir));
    #else
    	return (0);
    #endif
    }
    
    void
    p2p_input(struct ifnet *ifp, struct mbuf *m)
    {
    	void (*input)(struct ifnet *, struct mbuf *);
    
    	switch (m->m_pkthdr.ph_family) {
    	case AF_INET:
    		input = ipv4_input;
    		break;
    #ifdef INET6
    	case AF_INET6:
    		input = ipv6_input;
    		break;
    #endif
    #ifdef MPLS
    	case AF_MPLS:
    		input = mpls_input;
    		break;
    #endif
    	default:
    		m_freem(m);
    		return;
    	}
    
    	(*input)(ifp, m);
    }
    
    /*
     * Bring down all interfaces
     */
    void
    if_downall(void)
    {
    	struct ifreq ifrq;	/* XXX only partly built */
    	struct ifnet *ifp;
    
    	NET_LOCK();
    	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
    		if ((ifp->if_flags & IFF_UP) == 0)
    			continue;
    		if_down(ifp);
    		ifrq.ifr_flags = ifp->if_flags;
    		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
    	}
    	NET_UNLOCK();
    }
    
    /*
     * Mark an interface down and notify protocols of
     * the transition.
     */
    void
    if_down(struct ifnet *ifp)
    {
    	NET_ASSERT_LOCKED();
    
    	ifp->if_flags &= ~IFF_UP;
    	getmicrotime(&ifp->if_lastchange);
    	ifq_purge(&ifp->if_snd);
    
    	if_linkstate(ifp);
    }
    
    /*
     * Mark an interface up and notify protocols of
     * the transition.
     */
    void
    if_up(struct ifnet *ifp)
    {
    	NET_ASSERT_LOCKED();
    
    	ifp->if_flags |= IFF_UP;
    	getmicrotime(&ifp->if_lastchange);
    
    #ifdef INET6
    	/* Userland expects the kernel to set ::1 on default lo(4). */
    	if (ifp->if_index == rtable_loindex(ifp->if_rdomain))
    		in6_ifattach(ifp);
    #endif
    
    	if_linkstate(ifp);
    }
    
    /*
     * Notify userland, the routing table and hooks owner of
     * a link-state transition.
     */
    void
    if_linkstate_task(void *xifidx)
    {
    	unsigned int ifidx = (unsigned long)xifidx;
    	struct ifnet *ifp;
    
    	NET_LOCK();
    	KERNEL_LOCK();
    
    	ifp = if_get(ifidx);
    	if (ifp != NULL)
    		if_linkstate(ifp);
    	if_put(ifp);
    
    	KERNEL_UNLOCK();
    	NET_UNLOCK();
    }
    
    void
    if_linkstate(struct ifnet *ifp)
    {
    	NET_ASSERT_LOCKED();
    
    	rtm_ifchg(ifp);
    	rt_if_track(ifp);
    
    	if_hooks_run(&ifp->if_linkstatehooks);
    }
    
    void
    if_linkstatehook_add(struct ifnet *ifp, struct task *t)
    {
    	mtx_enter(&if_hooks_mtx);
    	TAILQ_INSERT_HEAD(&ifp->if_linkstatehooks, t, t_entry);
    	mtx_leave(&if_hooks_mtx);
    }
    
    void
    if_linkstatehook_del(struct ifnet *ifp, struct task *t)
    {
    	mtx_enter(&if_hooks_mtx);
    	TAILQ_REMOVE(&ifp->if_linkstatehooks, t, t_entry);
    	mtx_leave(&if_hooks_mtx);
    }
    
    /*
     * Schedule a link state change task.
     */
    void
    if_link_state_change(struct ifnet *ifp)
    {
    	task_add(net_tq(ifp->if_index), &ifp->if_linkstatetask);
    }
    
    /*
     * Handle interface watchdog timer routine.  Called
     * from softclock, we decrement timer (if set) and
     * call the appropriate interface routine on expiration.
     */
    void
    if_slowtimo(void *arg)
    {
    	struct ifnet *ifp = arg;
    	int s = splnet();
    
    	if (ifp->if_watchdog) {
    		if (ifp->if_timer > 0 && --ifp->if_timer == 0)
    			task_add(net_tq(ifp->if_index), &ifp->if_watchdogtask);
    		timeout_add_sec(&ifp->if_slowtimo, IFNET_SLOWTIMO);
    	}
    	splx(s);
    }
    
    void
    if_watchdog_task(void *xifidx)
    {
    	unsigned int ifidx = (unsigned long)xifidx;
    	struct ifnet *ifp;
    	int s;
    
    	ifp = if_get(ifidx);
    	if (ifp == NULL)
    		return;
    
    	KERNEL_LOCK();
    	s = splnet();
    	if (ifp->if_watchdog)
    		(*ifp->if_watchdog)(ifp);
    	splx(s);
    	KERNEL_UNLOCK();
    
    	if_put(ifp);
    }
    
    /*
     * Map interface name to interface structure pointer.
     */
    struct ifnet *
    if_unit(const char *name)
    {
    	struct ifnet *ifp;
    
    	KERNEL_ASSERT_LOCKED();
    
    	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
    		if (strcmp(ifp->if_xname, name) == 0) {
    			if_ref(ifp);
    			return (ifp);
    		}
    	}
    
    	return (NULL);
    }
    
    /*
     * Map interface index to interface structure pointer.
     */
    struct ifnet *
    if_get(unsigned int index)
    {
    	struct ifnet **if_map;
    	struct ifnet *ifp = NULL;
    
    	if (index == 0)
    		return (NULL);
    
    	smr_read_enter();
    	if_map = SMR_PTR_GET(&if_idxmap.map);
    	if (index < if_idxmap_limit(if_map)) {
    		ifp = SMR_PTR_GET(&if_map[index]);
    		if (ifp != NULL) {
    			KASSERT(ifp->if_index == index);
    			if_ref(ifp);
    		}
    	}
    	smr_read_leave();
    
    	return (ifp);
    }
    
    struct ifnet *
    if_ref(struct ifnet *ifp)
    {
    	refcnt_take(&ifp->if_refcnt);
    
    	return (ifp);
    }
    
    void
    if_put(struct ifnet *ifp)
    {
    	if (ifp == NULL)
    		return;
    
    	refcnt_rele_wake(&ifp->if_refcnt);
    }
    
    int
    if_setlladdr(struct ifnet *ifp, const uint8_t *lladdr)
    {
    	if (ifp->if_sadl == NULL)
    		return (EINVAL);
    
    	memcpy(((struct arpcom *)ifp)->ac_enaddr, lladdr, ETHER_ADDR_LEN);
    	memcpy(LLADDR(ifp->if_sadl), lladdr, ETHER_ADDR_LEN);
    
    	return (0);
    }
    
    int
    if_createrdomain(int rdomain, struct ifnet *ifp)
    {
    	int error;
    	struct ifnet *loifp;
    	char loifname[IFNAMSIZ];
    	unsigned int unit = rdomain;
    
    	if ((error = rtable_add(rdomain)) != 0)
    		return (error);
    	if (!rtable_empty(rdomain))
    		return (EEXIST);
    
    	/* Create rdomain including its loopback if with unit == rdomain */
    	snprintf(loifname, sizeof(loifname), "lo%u", unit);
    	error = if_clone_create(loifname, 0);
    	if ((loifp = if_unit(loifname)) == NULL)
    		return (ENXIO);
    	if (error && (ifp != loifp || error != EEXIST)) {
    		if_put(loifp);
    		return (error);
    	}
    
    	rtable_l2set(rdomain, rdomain, loifp->if_index);
    	loifp->if_rdomain = rdomain;
    	if_put(loifp);
    
    	return (0);
    }
    
    int
    if_setrdomain(struct ifnet *ifp, int rdomain)
    {
    	struct ifreq ifr;
    	int error, up = 0, s;
    
    	if (rdomain < 0 || rdomain > RT_TABLEID_MAX)
    		return (EINVAL);
    
    	if (rdomain != ifp->if_rdomain &&
    	    (ifp->if_flags & IFF_LOOPBACK) &&
    	    (ifp->if_index == rtable_loindex(ifp->if_rdomain)))
    		return (EPERM);
    
    	if (!rtable_exists(rdomain))
    		return (ESRCH);
    
    	/* make sure that the routing table is a real rdomain */
    	if (rdomain != rtable_l2(rdomain))
    		return (EINVAL);
    
    	if (rdomain != ifp->if_rdomain) {
    		s = splnet();
    		/*
    		 * We are tearing down the world.
    		 * Take down the IF so:
    		 * 1. everything that cares gets a message
    		 * 2. the automagic IPv6 bits are recreated
    		 */
    		if (ifp->if_flags & IFF_UP) {
    			up = 1;
    			if_down(ifp);
    		}
    		rti_delete(ifp);
    #ifdef MROUTING
    		vif_delete(ifp);
    #endif
    		in_ifdetach(ifp);
    #ifdef INET6
    		in6_ifdetach(ifp);
    #endif
    		splx(s);
    	}
    
    	/* Let devices like enc(4) or mpe(4) know about the change */
    	ifr.ifr_rdomainid = rdomain;
    	if ((error = (*ifp->if_ioctl)(ifp, SIOCSIFRDOMAIN,
    	    (caddr_t)&ifr)) != ENOTTY)
    		return (error);
    	error = 0;
    
    	/* Add interface to the specified rdomain */
    	ifp->if_rdomain = rdomain;
    
    	/* If we took down the IF, bring it back */
    	if (up) {
    		s = splnet();
    		if_up(ifp);
    		splx(s);
    	}
    
    	return (0);
    }
    
    /*
     * Interface ioctls.
     */
    int
    ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
    {
    	struct ifnet *ifp;
    	struct ifreq *ifr = (struct ifreq *)data;
    	struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
    	struct if_afreq *ifar = (struct if_afreq *)data;
    	char ifdescrbuf[IFDESCRSIZE];
    	char ifrtlabelbuf[RTLABEL_LEN];
    	int s, error = 0, oif_xflags;
    	size_t bytesdone;
    	unsigned short oif_flags;
    
    	switch (cmd) {
    	case SIOCIFCREATE:
    		if ((error = suser(p)) != 0)
    			return (error);
    		KERNEL_LOCK();
    		error = if_clone_create(ifr->ifr_name, 0);
    		KERNEL_UNLOCK();
    		return (error);
    	case SIOCIFDESTROY:
    		if ((error = suser(p)) != 0)
    			return (error);
    		KERNEL_LOCK();
    		error = if_clone_destroy(ifr->ifr_name);
    		KERNEL_UNLOCK();
    		return (error);
    	case SIOCSIFGATTR:
    		if ((error = suser(p)) != 0)
    			return (error);
    		KERNEL_LOCK();
    		NET_LOCK();
    		error = if_setgroupattribs(data);
    		NET_UNLOCK();
    		KERNEL_UNLOCK();
    		return (error);
    	case SIOCGIFCONF:
    	case SIOCIFGCLONERS:
    	case SIOCGIFGMEMB:
    	case SIOCGIFGATTR:
    	case SIOCGIFGLIST:
    	case SIOCGIFFLAGS:
    	case SIOCGIFXFLAGS:
    	case SIOCGIFMETRIC:
    	case SIOCGIFMTU:
    	case SIOCGIFHARDMTU:
    	case SIOCGIFDATA:
    	case SIOCGIFDESCR:
    	case SIOCGIFRTLABEL:
    	case SIOCGIFPRIORITY:
    	case SIOCGIFRDOMAIN:
    	case SIOCGIFGROUP:
    	case SIOCGIFLLPRIO:
    		error = ifioctl_get(cmd, data);
    		return (error);
    	}
    
    	KERNEL_LOCK();
    
    	ifp = if_unit(ifr->ifr_name);
    	if (ifp == NULL) {
    		KERNEL_UNLOCK();
    		return (ENXIO);
    	}
    	oif_flags = ifp->if_flags;
    	oif_xflags = ifp->if_xflags;
    
    	switch (cmd) {
    	case SIOCIFAFATTACH:
    	case SIOCIFAFDETACH:
    		if ((error = suser(p)) != 0)
    			break;
    		NET_LOCK();
    		switch (ifar->ifar_af) {
    		case AF_INET:
    			/* attach is a noop for AF_INET */
    			if (cmd == SIOCIFAFDETACH)
    				in_ifdetach(ifp);
    			break;
    #ifdef INET6
    		case AF_INET6:
    			if (cmd == SIOCIFAFATTACH)
    				error = in6_ifattach(ifp);
    			else
    				in6_ifdetach(ifp);
    			break;
    #endif /* INET6 */
    		default:
    			error = EAFNOSUPPORT;
    		}
    		NET_UNLOCK();
    		break;
    
    	case SIOCSIFXFLAGS:
    		if ((error = suser(p)) != 0)
    			break;
    
    		NET_LOCK();
    #ifdef INET6
    		if ((ISSET(ifr->ifr_flags, IFXF_AUTOCONF6) ||
    		    ISSET(ifr->ifr_flags, IFXF_AUTOCONF6TEMP)) &&
    		    !ISSET(ifp->if_xflags, IFXF_AUTOCONF6) &&
    		    !ISSET(ifp->if_xflags, IFXF_AUTOCONF6TEMP)) {
    			error = in6_ifattach(ifp);
    			if (error != 0) {
    				NET_UNLOCK();
    				break;
    			}
    		}
    
    		if (ISSET(ifr->ifr_flags, IFXF_INET6_NOSOII) &&
    		    !ISSET(ifp->if_xflags, IFXF_INET6_NOSOII))
    			ifp->if_xflags |= IFXF_INET6_NOSOII;
    
    		if (!ISSET(ifr->ifr_flags, IFXF_INET6_NOSOII) &&
    		    ISSET(ifp->if_xflags, IFXF_INET6_NOSOII))
    			ifp->if_xflags &= ~IFXF_INET6_NOSOII;
    
    #endif	/* INET6 */
    
    #ifdef MPLS
    		if (ISSET(ifr->ifr_flags, IFXF_MPLS) &&
    		    !ISSET(ifp->if_xflags, IFXF_MPLS)) {
    			s = splnet();
    			ifp->if_xflags |= IFXF_MPLS;
    			ifp->if_ll_output = ifp->if_output;
    			ifp->if_output = mpls_output;
    			splx(s);
    		}
    		if (ISSET(ifp->if_xflags, IFXF_MPLS) &&
    		    !ISSET(ifr->ifr_flags, IFXF_MPLS)) {
    			s = splnet();
    			ifp->if_xflags &= ~IFXF_MPLS;
    			ifp->if_output = ifp->if_ll_output;
    			ifp->if_ll_output = NULL;
    			splx(s);
    		}
    #endif	/* MPLS */
    
    #ifndef SMALL_KERNEL
    		if (ifp->if_capabilities & IFCAP_WOL) {
    			if (ISSET(ifr->ifr_flags, IFXF_WOL) &&
    			    !ISSET(ifp->if_xflags, IFXF_WOL)) {
    				s = splnet();
    				ifp->if_xflags |= IFXF_WOL;
    				error = ifp->if_wol(ifp, 1);
    				splx(s);
    			}
    			if (ISSET(ifp->if_xflags, IFXF_WOL) &&
    			    !ISSET(ifr->ifr_flags, IFXF_WOL)) {
    				s = splnet();
    				ifp->if_xflags &= ~IFXF_WOL;
    				error = ifp->if_wol(ifp, 0);
    				splx(s);
    			}
    		} else if (ISSET(ifr->ifr_flags, IFXF_WOL)) {
    			ifr->ifr_flags &= ~IFXF_WOL;
    			error = ENOTSUP;
    		}
    #endif
    		if (ISSET(ifr->ifr_flags, IFXF_LRO) !=
    		    ISSET(ifp->if_xflags, IFXF_LRO))
    			error = ifsetlro(ifp, ISSET(ifr->ifr_flags, IFXF_LRO));
    
    		if (error == 0)
    			ifp->if_xflags = (ifp->if_xflags & IFXF_CANTCHANGE) |
    				(ifr->ifr_flags & ~IFXF_CANTCHANGE);
    
    		if (!ISSET(ifp->if_flags, IFF_UP) &&
    		    ((!ISSET(oif_xflags, IFXF_AUTOCONF4) &&
    		    ISSET(ifp->if_xflags, IFXF_AUTOCONF4)) ||
    		    (!ISSET(oif_xflags, IFXF_AUTOCONF6) &&
    		    ISSET(ifp->if_xflags, IFXF_AUTOCONF6)) ||
    		    (!ISSET(oif_xflags, IFXF_AUTOCONF6TEMP) &&
    		    ISSET(ifp->if_xflags, IFXF_AUTOCONF6TEMP)))) {
    			ifr->ifr_flags = ifp->if_flags | IFF_UP;
    			goto forceup;
    		}
    
    		NET_UNLOCK();
    		break;
    
    	case SIOCSIFFLAGS:
    		if ((error = suser(p)) != 0)
    			break;
    
    		NET_LOCK();
    forceup:
    		ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
    			(ifr->ifr_flags & ~IFF_CANTCHANGE);
    		error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, data);
    		if (error != 0) {
    			ifp->if_flags = oif_flags;
    			if (cmd == SIOCSIFXFLAGS)
    				ifp->if_xflags = oif_xflags;
    		} else if (ISSET(oif_flags ^ ifp->if_flags, IFF_UP)) {
    			s = splnet();
    			if (ISSET(ifp->if_flags, IFF_UP))
    				if_up(ifp);
    			else
    				if_down(ifp);
    			splx(s);
    		}
    		NET_UNLOCK();
    		break;
    
    	case SIOCSIFMETRIC:
    		if ((error = suser(p)) != 0)
    			break;
    		NET_LOCK();
    		ifp->if_metric = ifr->ifr_metric;
    		NET_UNLOCK();
    		break;
    
    	case SIOCSIFMTU:
    		if ((error = suser(p)) != 0)
    			break;
    		NET_LOCK();
    		error = (*ifp->if_ioctl)(ifp, cmd, data);
    		NET_UNLOCK();
    		if (error == 0)
    			rtm_ifchg(ifp);
    		break;
    
    	case SIOCSIFDESCR:
    		if ((error = suser(p)) != 0)
    			break;
    		error = copyinstr(ifr->ifr_data, ifdescrbuf,
    		    IFDESCRSIZE, &bytesdone);
    		if (error == 0) {
    			(void)memset(ifp->if_description, 0, IFDESCRSIZE);
    			strlcpy(ifp->if_description, ifdescrbuf, IFDESCRSIZE);
    		}
    		break;
    
    	case SIOCSIFRTLABEL:
    		if ((error = suser(p)) != 0)
    			break;
    		error = copyinstr(ifr->ifr_data, ifrtlabelbuf,
    		    RTLABEL_LEN, &bytesdone);
    		if (error == 0) {
    			rtlabel_unref(ifp->if_rtlabelid);
    			ifp->if_rtlabelid = rtlabel_name2id(ifrtlabelbuf);
    		}
    		break;
    
    	case SIOCSIFPRIORITY:
    		if ((error = suser(p)) != 0)
    			break;
    		if (ifr->ifr_metric < 0 || ifr->ifr_metric > 15) {
    			error = EINVAL;
    			break;
    		}
    		ifp->if_priority = ifr->ifr_metric;
    		break;
    
    	case SIOCSIFRDOMAIN:
    		if ((error = suser(p)) != 0)
    			break;
    		error = if_createrdomain(ifr->ifr_rdomainid, ifp);
    		if (!error || error == EEXIST) {
    			NET_LOCK();
    			error = if_setrdomain(ifp, ifr->ifr_rdomainid);
    			NET_UNLOCK();
    		}
    		break;
    
    	case SIOCAIFGROUP:
    		if ((error = suser(p)))
    			break;
    		NET_LOCK();
    		error = if_addgroup(ifp, ifgr->ifgr_group);
    		if (error == 0) {
    			error = (*ifp->if_ioctl)(ifp, cmd, data);
    			if (error == ENOTTY)
    				error = 0;
    		}
    		NET_UNLOCK();
    		break;
    
    	case SIOCDIFGROUP:
    		if ((error = suser(p)))
    			break;
    		NET_LOCK();
    		error = (*ifp->if_ioctl)(ifp, cmd, data);
    		if (error == ENOTTY)
    			error = 0;
    		if (error == 0)
    			error = if_delgroup(ifp, ifgr->ifgr_group);
    		NET_UNLOCK();
    		break;
    
    	case SIOCSIFLLADDR:
    		if ((error = suser(p)))
    			break;
    		if ((ifp->if_sadl == NULL) ||
    		    (ifr->ifr_addr.sa_len != ETHER_ADDR_LEN) ||
    		    (ETHER_IS_MULTICAST(ifr->ifr_addr.sa_data))) {
    			error = EINVAL;
    			break;
    		}
    		NET_LOCK();
    		switch (ifp->if_type) {
    		case IFT_ETHER:
    		case IFT_CARP:
    		case IFT_XETHER:
    		case IFT_ISO88025:
    			error = (*ifp->if_ioctl)(ifp, cmd, data);
    			if (error == ENOTTY)
    				error = 0;
    			if (error == 0)
    				error = if_setlladdr(ifp,
    				    ifr->ifr_addr.sa_data);
    			break;
    		default:
    			error = ENODEV;
    		}
    
    		if (error == 0)
    			ifnewlladdr(ifp);
    		NET_UNLOCK();
    		if (error == 0)
    			rtm_ifchg(ifp);
    		break;
    
    	case SIOCSIFLLPRIO:
    		if ((error = suser(p)))
    			break;
    		if (ifr->ifr_llprio < IFQ_MINPRIO ||
    		    ifr->ifr_llprio > IFQ_MAXPRIO) {
    			error = EINVAL;
    			break;
    		}
    		NET_LOCK();
    		ifp->if_llprio = ifr->ifr_llprio;
    		NET_UNLOCK();
    		break;
    
    	case SIOCGIFSFFPAGE:
    		error = suser(p);
    		if (error != 0)
    			break;
    
    		error = if_sffpage_check(data);
    		if (error != 0)
    			break;
    
    		/* don't take NET_LOCK because i2c reads take a long time */
    		error = ((*ifp->if_ioctl)(ifp, cmd, data));
    		break;
    
    	case SIOCSIFMEDIA:
    		if ((error = suser(p)) != 0)
    			break;
    		/* FALLTHROUGH */
    	case SIOCGIFMEDIA:
    		/* net lock is not needed */
    		error = ((*ifp->if_ioctl)(ifp, cmd, data));
    		break;
    
    	case SIOCSETKALIVE:
    	case SIOCDIFPHYADDR:
    	case SIOCSLIFPHYADDR:
    	case SIOCSLIFPHYRTABLE:
    	case SIOCSLIFPHYTTL:
    	case SIOCSLIFPHYDF:
    	case SIOCSLIFPHYECN:
    	case SIOCADDMULTI:
    	case SIOCDELMULTI:
    	case SIOCSVNETID:
    	case SIOCDVNETID:
    	case SIOCSVNETFLOWID:
    	case SIOCSTXHPRIO:
    	case SIOCSRXHPRIO:
    	case SIOCSIFPAIR:
    	case SIOCSIFPARENT:
    	case SIOCDIFPARENT:
    	case SIOCSETMPWCFG:
    	case SIOCSETLABEL:
    	case SIOCDELLABEL:
    	case SIOCSPWE3CTRLWORD:
    	case SIOCSPWE3FAT:
    	case SIOCSPWE3NEIGHBOR:
    	case SIOCDPWE3NEIGHBOR:
    #if NBRIDGE > 0
    	case SIOCBRDGADD:
    	case SIOCBRDGDEL:
    	case SIOCBRDGSIFFLGS:
    	case SIOCBRDGSCACHE:
    	case SIOCBRDGADDS:
    	case SIOCBRDGDELS:
    	case SIOCBRDGSADDR:
    	case SIOCBRDGSTO:
    	case SIOCBRDGDADDR:
    	case SIOCBRDGFLUSH:
    	case SIOCBRDGADDL:
    	case SIOCBRDGSIFPROT:
    	case SIOCBRDGARL:
    	case SIOCBRDGFRL:
    	case SIOCBRDGSPRI:
    	case SIOCBRDGSHT:
    	case SIOCBRDGSFD:
    	case SIOCBRDGSMA:
    	case SIOCBRDGSIFPRIO:
    	case SIOCBRDGSIFCOST:
    	case SIOCBRDGSTXHC:
    	case SIOCBRDGSPROTO:
    #endif
    		if ((error = suser(p)) != 0)
    			break;
    		/* FALLTHROUGH */
    	default:
    		error = pru_control(so, cmd, data, ifp);
    		if (error != EOPNOTSUPP)
    			break;
    		switch (cmd) {
    		case SIOCAIFADDR:
    		case SIOCDIFADDR:
    		case SIOCSIFADDR:
    		case SIOCSIFNETMASK:
    		case SIOCSIFDSTADDR:
    		case SIOCSIFBRDADDR:
    #ifdef INET6
    		case SIOCAIFADDR_IN6:
    		case SIOCDIFADDR_IN6:
    #endif
    			error = suser(p);
    			break;
    		default:
    			error = 0;
    			break;
    		}
    		if (error)
    			break;
    		NET_LOCK();
    		error = ((*ifp->if_ioctl)(ifp, cmd, data));
    		NET_UNLOCK();
    		break;
    	}
    
    	if (oif_flags != ifp->if_flags || oif_xflags != ifp->if_xflags) {
    		/* if_up() and if_down() already sent an update, skip here */
    		if (((oif_flags ^ ifp->if_flags) & IFF_UP) == 0)
    			rtm_ifchg(ifp);
    	}
    
    	if (((oif_flags ^ ifp->if_flags) & IFF_UP) != 0)
    		getmicrotime(&ifp->if_lastchange);
    
    	KERNEL_UNLOCK();
    
    	if_put(ifp);
    
    	return (error);
    }
    
    int
    ifioctl_get(u_long cmd, caddr_t data)
    {
    	struct ifnet *ifp;
    	struct ifreq *ifr = (struct ifreq *)data;
    	char ifdescrbuf[IFDESCRSIZE];
    	char ifrtlabelbuf[RTLABEL_LEN];
    	int error = 0;
    	size_t bytesdone;
    
    	switch(cmd) {
    	case SIOCGIFCONF:
    		NET_LOCK_SHARED();
    		error = ifconf(data);
    		NET_UNLOCK_SHARED();
    		return (error);
    	case SIOCIFGCLONERS:
    		error = if_clone_list((struct if_clonereq *)data);
    		return (error);
    	case SIOCGIFGMEMB:
    		NET_LOCK_SHARED();
    		error = if_getgroupmembers(data);
    		NET_UNLOCK_SHARED();
    		return (error);
    	case SIOCGIFGATTR:
    		NET_LOCK_SHARED();
    		error = if_getgroupattribs(data);
    		NET_UNLOCK_SHARED();
    		return (error);
    	case SIOCGIFGLIST:
    		NET_LOCK_SHARED();
    		error = if_getgrouplist(data);
    		NET_UNLOCK_SHARED();
    		return (error);
    	}
    
    	KERNEL_LOCK();
    
    	ifp = if_unit(ifr->ifr_name);
    	if (ifp == NULL) {
    		KERNEL_UNLOCK();
    		return (ENXIO);
    	}
    
    	NET_LOCK_SHARED();
    
    	switch(cmd) {
    	case SIOCGIFFLAGS:
    		ifr->ifr_flags = ifp->if_flags;
    		if (ifq_is_oactive(&ifp->if_snd))
    			ifr->ifr_flags |= IFF_OACTIVE;
    		break;
    
    	case SIOCGIFXFLAGS:
    		ifr->ifr_flags = ifp->if_xflags & ~(IFXF_MPSAFE|IFXF_CLONED);
    		break;
    
    	case SIOCGIFMETRIC:
    		ifr->ifr_metric = ifp->if_metric;
    		break;
    
    	case SIOCGIFMTU:
    		ifr->ifr_mtu = ifp->if_mtu;
    		break;
    
    	case SIOCGIFHARDMTU:
    		ifr->ifr_hardmtu = ifp->if_hardmtu;
    		break;
    
    	case SIOCGIFDATA: {
    		struct if_data ifdata;
    		if_getdata(ifp, &ifdata);
    		error = copyout(&ifdata, ifr->ifr_data, sizeof(ifdata));
    		break;
    	}
    
    	case SIOCGIFDESCR:
    		strlcpy(ifdescrbuf, ifp->if_description, IFDESCRSIZE);
    		error = copyoutstr(ifdescrbuf, ifr->ifr_data, IFDESCRSIZE,
    		    &bytesdone);
    		break;
    
    	case SIOCGIFRTLABEL:
    		if (ifp->if_rtlabelid && rtlabel_id2name(ifp->if_rtlabelid,
    		    ifrtlabelbuf, RTLABEL_LEN) != NULL) {
    			error = copyoutstr(ifrtlabelbuf, ifr->ifr_data,
    			    RTLABEL_LEN, &bytesdone);
    		} else
    			error = ENOENT;
    		break;
    
    	case SIOCGIFPRIORITY:
    		ifr->ifr_metric = ifp->if_priority;
    		break;
    
    	case SIOCGIFRDOMAIN:
    		ifr->ifr_rdomainid = ifp->if_rdomain;
    		break;
    
    	case SIOCGIFGROUP:
    		error = if_getgroup(data, ifp);
    		break;
    
    	case SIOCGIFLLPRIO:
    		ifr->ifr_llprio = ifp->if_llprio;
    		break;
    
    	default:
    		panic("invalid ioctl %lu", cmd);
    	}
    
    	NET_UNLOCK_SHARED();
    
    	KERNEL_UNLOCK();
    
    	if_put(ifp);
    
    	return (error);
    }
    
    static int
    if_sffpage_check(const caddr_t data)
    {
    	const struct if_sffpage *sff = (const struct if_sffpage *)data;
    
    	switch (sff->sff_addr) {
    	case IFSFF_ADDR_EEPROM:
    	case IFSFF_ADDR_DDM:
    		break;
    	default:
    		return (EINVAL);
    	}
    
    	return (0);
    }
    
    int
    if_txhprio_l2_check(int hdrprio)
    {
    	switch (hdrprio) {
    	case IF_HDRPRIO_PACKET:
    		return (0);
    	default:
    		if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
    			return (0);
    		break;
    	}
    
    	return (EINVAL);
    }
    
    int
    if_txhprio_l3_check(int hdrprio)
    {
    	switch (hdrprio) {
    	case IF_HDRPRIO_PACKET:
    	case IF_HDRPRIO_PAYLOAD:
    		return (0);
    	default:
    		if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
    			return (0);
    		break;
    	}
    
    	return (EINVAL);
    }
    
    int
    if_rxhprio_l2_check(int hdrprio)
    {
    	switch (hdrprio) {
    	case IF_HDRPRIO_PACKET:
    	case IF_HDRPRIO_OUTER:
    		return (0);
    	default:
    		if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
    			return (0);
    		break;
    	}
    
    	return (EINVAL);
    }
    
    int
    if_rxhprio_l3_check(int hdrprio)
    {
    	switch (hdrprio) {
    	case IF_HDRPRIO_PACKET:
    	case IF_HDRPRIO_PAYLOAD:
    	case IF_HDRPRIO_OUTER:
    		return (0);
    	default:
    		if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
    			return (0);
    		break;
    	}
    
    	return (EINVAL);
    }
    
    /*
     * Return interface configuration
     * of system.  List may be used
     * in later ioctl's (above) to get
     * other information.
     */
    int
    ifconf(caddr_t data)
    {
    	struct ifconf *ifc = (struct ifconf *)data;
    	struct ifnet *ifp;
    	struct ifaddr *ifa;
    	struct ifreq ifr, *ifrp;
    	int space = ifc->ifc_len, error = 0;
    
    	/* If ifc->ifc_len is 0, fill it in with the needed size and return. */
    	if (space == 0) {
    		TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
    			struct sockaddr *sa;
    
    			if (TAILQ_EMPTY(&ifp->if_addrlist))
    				space += sizeof (ifr);
    			else
    				TAILQ_FOREACH(ifa,
    				    &ifp->if_addrlist, ifa_list) {
    					sa = ifa->ifa_addr;
    					if (sa->sa_len > sizeof(*sa))
    						space += sa->sa_len -
    						    sizeof(*sa);
    					space += sizeof(ifr);
    				}
    		}
    		ifc->ifc_len = space;
    		return (0);
    	}
    
    	ifrp = ifc->ifc_req;
    	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
    		if (space < sizeof(ifr))
    			break;
    		bcopy(ifp->if_xname, ifr.ifr_name, IFNAMSIZ);
    		if (TAILQ_EMPTY(&ifp->if_addrlist)) {
    			bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
    			error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
    			    sizeof(ifr));
    			if (error)
    				break;
    			space -= sizeof (ifr), ifrp++;
    		} else
    			TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
    				struct sockaddr *sa = ifa->ifa_addr;
    
    				if (space < sizeof(ifr))
    					break;
    				if (sa->sa_len <= sizeof(*sa)) {
    					ifr.ifr_addr = *sa;
    					error = copyout((caddr_t)&ifr,
    					    (caddr_t)ifrp, sizeof (ifr));
    					ifrp++;
    				} else {
    					space -= sa->sa_len - sizeof(*sa);
    					if (space < sizeof (ifr))
    						break;
    					error = copyout((caddr_t)&ifr,
    					    (caddr_t)ifrp,
    					    sizeof(ifr.ifr_name));
    					if (error == 0)
    						error = copyout((caddr_t)sa,
    						    (caddr_t)&ifrp->ifr_addr,
    						    sa->sa_len);
    					ifrp = (struct ifreq *)(sa->sa_len +
    					    (caddr_t)&ifrp->ifr_addr);
    				}
    				if (error)
    					break;
    				space -= sizeof (ifr);
    			}
    	}
    	ifc->ifc_len -= space;
    	return (error);
    }
    
    void
    if_counters_alloc(struct ifnet *ifp)
    {
    	KASSERT(ifp->if_counters == NULL);
    
    	ifp->if_counters = counters_alloc(ifc_ncounters);
    }
    
    void
    if_counters_free(struct ifnet *ifp)
    {
    	KASSERT(ifp->if_counters != NULL);
    
    	counters_free(ifp->if_counters, ifc_ncounters);
    	ifp->if_counters = NULL;
    }
    
    void
    if_getdata(struct ifnet *ifp, struct if_data *data)
    {
    	unsigned int i;
    
    	*data = ifp->if_data;
    
    	if (ifp->if_counters != NULL) {
    		uint64_t counters[ifc_ncounters];
    
    		counters_read(ifp->if_counters, counters, nitems(counters),
    		    NULL);
    
    		data->ifi_ipackets += counters[ifc_ipackets];
    		data->ifi_ierrors += counters[ifc_ierrors];
    		data->ifi_opackets += counters[ifc_opackets];
    		data->ifi_oerrors += counters[ifc_oerrors];
    		data->ifi_collisions += counters[ifc_collisions];
    		data->ifi_ibytes += counters[ifc_ibytes];
    		data->ifi_obytes += counters[ifc_obytes];
    		data->ifi_imcasts += counters[ifc_imcasts];
    		data->ifi_omcasts += counters[ifc_omcasts];
    		data->ifi_iqdrops += counters[ifc_iqdrops];
    		data->ifi_oqdrops += counters[ifc_oqdrops];
    		data->ifi_noproto += counters[ifc_noproto];
    	}
    
    	for (i = 0; i < ifp->if_nifqs; i++) {
    		struct ifqueue *ifq = ifp->if_ifqs[i];
    
    		ifq_add_data(ifq, data);
    	}
    
    	for (i = 0; i < ifp->if_niqs; i++) {
    		struct ifiqueue *ifiq = ifp->if_iqs[i];
    
    		ifiq_add_data(ifiq, data);
    	}
    }
    
    /*
     * Dummy functions replaced in ifnet during detach (if protocols decide to
     * fiddle with the if during detach.
     */
    void
    if_detached_qstart(struct ifqueue *ifq)
    {
    	ifq_purge(ifq);
    }
    
    int
    if_detached_ioctl(struct ifnet *ifp, u_long a, caddr_t b)
    {
    	return ENODEV;
    }
    
    /*
     * Create interface group without members
     */
    struct ifg_group *
    if_creategroup(const char *groupname)
    {
    	struct ifg_group	*ifg;
    
    	if ((ifg = malloc(sizeof(*ifg), M_IFGROUP, M_NOWAIT)) == NULL)
    		return (NULL);
    
    	strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group));
    	ifg->ifg_refcnt = 1;
    	ifg->ifg_carp_demoted = 0;
    	TAILQ_INIT(&ifg->ifg_members);
    #if NPF > 0
    	pfi_attach_ifgroup(ifg);
    #endif
    	TAILQ_INSERT_TAIL(&ifg_head, ifg, ifg_next);
    
    	return (ifg);
    }
    
    /*
     * Add a group to an interface
     */
    int
    if_addgroup(struct ifnet *ifp, const char *groupname)
    {
    	struct ifg_list		*ifgl;
    	struct ifg_group	*ifg = NULL;
    	struct ifg_member	*ifgm;
    	size_t			 namelen;
    
    	namelen = strlen(groupname);
    	if (namelen == 0 || namelen >= IFNAMSIZ ||
    	    (groupname[namelen - 1] >= '0' && groupname[namelen - 1] <= '9'))
    		return (EINVAL);
    
    	TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
    		if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
    			return (EEXIST);
    
    	if ((ifgl = malloc(sizeof(*ifgl), M_IFGROUP, M_NOWAIT)) == NULL)
    		return (ENOMEM);
    
    	if ((ifgm = malloc(sizeof(*ifgm), M_IFGROUP, M_NOWAIT)) == NULL) {
    		free(ifgl, M_IFGROUP, sizeof(*ifgl));
    		return (ENOMEM);
    	}
    
    	TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
    		if (!strcmp(ifg->ifg_group, groupname))
    			break;
    
    	if (ifg == NULL) {
    		ifg = if_creategroup(groupname);
    		if (ifg == NULL) {
    			free(ifgl, M_IFGROUP, sizeof(*ifgl));
    			free(ifgm, M_IFGROUP, sizeof(*ifgm));
    			return (ENOMEM);
    		}
    	} else
    		ifg->ifg_refcnt++;
    	KASSERT(ifg->ifg_refcnt != 0);
    
    	ifgl->ifgl_group = ifg;
    	ifgm->ifgm_ifp = ifp;
    
    	TAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next);
    	TAILQ_INSERT_TAIL(&ifp->if_groups, ifgl, ifgl_next);
    
    #if NPF > 0
    	pfi_group_addmember(groupname);
    #endif
    
    	return (0);
    }
    
    /*
     * Remove a group from an interface
     */
    int
    if_delgroup(struct ifnet *ifp, const char *groupname)
    {
    	struct ifg_list		*ifgl;
    	struct ifg_member	*ifgm;
    
    	TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
    		if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
    			break;
    	if (ifgl == NULL)
    		return (ENOENT);
    
    	TAILQ_REMOVE(&ifp->if_groups, ifgl, ifgl_next);
    
    	TAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next)
    		if (ifgm->ifgm_ifp == ifp)
    			break;
    
    	if (ifgm != NULL) {
    		TAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifgm_next);
    		free(ifgm, M_IFGROUP, sizeof(*ifgm));
    	}
    
    #if NPF > 0
    	pfi_group_delmember(groupname);
    #endif
    
    	KASSERT(ifgl->ifgl_group->ifg_refcnt != 0);
    	if (--ifgl->ifgl_group->ifg_refcnt == 0) {
    		TAILQ_REMOVE(&ifg_head, ifgl->ifgl_group, ifg_next);
    #if NPF > 0
    		pfi_detach_ifgroup(ifgl->ifgl_group);
    #endif
    		free(ifgl->ifgl_group, M_IFGROUP, sizeof(*ifgl->ifgl_group));
    	}
    
    	free(ifgl, M_IFGROUP, sizeof(*ifgl));
    
    	return (0);
    }
    
    /*
     * Stores all groups from an interface in memory pointed
     * to by data
     */
    int
    if_getgroup(caddr_t data, struct ifnet *ifp)
    {
    	int			 len, error;
    	struct ifg_list		*ifgl;
    	struct ifg_req		 ifgrq, *ifgp;
    	struct ifgroupreq	*ifgr = (struct ifgroupreq *)data;
    
    	if (ifgr->ifgr_len == 0) {
    		TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
    			ifgr->ifgr_len += sizeof(struct ifg_req);
    		return (0);
    	}
    
    	len = ifgr->ifgr_len;
    	ifgp = ifgr->ifgr_groups;
    	TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) {
    		if (len < sizeof(ifgrq))
    			return (EINVAL);
    		bzero(&ifgrq, sizeof ifgrq);
    		strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group,
    		    sizeof(ifgrq.ifgrq_group));
    		if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
    		    sizeof(struct ifg_req))))
    			return (error);
    		len -= sizeof(ifgrq);
    		ifgp++;
    	}
    
    	return (0);
    }
    
    /*
     * Stores all members of a group in memory pointed to by data
     */
    int
    if_getgroupmembers(caddr_t data)
    {
    	struct ifgroupreq	*ifgr = (struct ifgroupreq *)data;
    	struct ifg_group	*ifg;
    	struct ifg_member	*ifgm;
    	struct ifg_req		 ifgrq, *ifgp;
    	int			 len, error;
    
    	TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
    		if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
    			break;
    	if (ifg == NULL)
    		return (ENOENT);
    
    	if (ifgr->ifgr_len == 0) {
    		TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next)
    			ifgr->ifgr_len += sizeof(ifgrq);
    		return (0);
    	}
    
    	len = ifgr->ifgr_len;
    	ifgp = ifgr->ifgr_groups;
    	TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) {
    		if (len < sizeof(ifgrq))
    			return (EINVAL);
    		bzero(&ifgrq, sizeof ifgrq);
    		strlcpy(ifgrq.ifgrq_member, ifgm->ifgm_ifp->if_xname,
    		    sizeof(ifgrq.ifgrq_member));
    		if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
    		    sizeof(struct ifg_req))))
    			return (error);
    		len -= sizeof(ifgrq);
    		ifgp++;
    	}
    
    	return (0);
    }
    
    int
    if_getgroupattribs(caddr_t data)
    {
    	struct ifgroupreq	*ifgr = (struct ifgroupreq *)data;
    	struct ifg_group	*ifg;
    
    	TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
    		if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
    			break;
    	if (ifg == NULL)
    		return (ENOENT);
    
    	ifgr->ifgr_attrib.ifg_carp_demoted = ifg->ifg_carp_demoted;
    
    	return (0);
    }
    
    int
    if_setgroupattribs(caddr_t data)
    {
    	struct ifgroupreq	*ifgr = (struct ifgroupreq *)data;
    	struct ifg_group	*ifg;
    	struct ifg_member	*ifgm;
    	int			 demote;
    
    	TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
    		if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
    			break;
    	if (ifg == NULL)
    		return (ENOENT);
    
    	demote = ifgr->ifgr_attrib.ifg_carp_demoted;
    	if (demote + ifg->ifg_carp_demoted > 0xff ||
    	    demote + ifg->ifg_carp_demoted < 0)
    		return (EINVAL);
    
    	ifg->ifg_carp_demoted += demote;
    
    	TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next)
    		ifgm->ifgm_ifp->if_ioctl(ifgm->ifgm_ifp, SIOCSIFGATTR, data);
    
    	return (0);
    }
    
    /*
     * Stores all groups in memory pointed to by data
     */
    int
    if_getgrouplist(caddr_t data)
    {
    	struct ifgroupreq	*ifgr = (struct ifgroupreq *)data;
    	struct ifg_group	*ifg;
    	struct ifg_req		 ifgrq, *ifgp;
    	int			 len, error;
    
    	if (ifgr->ifgr_len == 0) {
    		TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
    			ifgr->ifgr_len += sizeof(ifgrq);
    		return (0);
    	}
    
    	len = ifgr->ifgr_len;
    	ifgp = ifgr->ifgr_groups;
    	TAILQ_FOREACH(ifg, &ifg_head, ifg_next) {
    		if (len < sizeof(ifgrq))
    			return (EINVAL);
    		bzero(&ifgrq, sizeof ifgrq);
    		strlcpy(ifgrq.ifgrq_group, ifg->ifg_group,
    		    sizeof(ifgrq.ifgrq_group));
    		if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
    		    sizeof(struct ifg_req))))
    			return (error);
    		len -= sizeof(ifgrq);
    		ifgp++;
    	}
    
    	return (0);
    }
    
    void
    if_group_routechange(const struct sockaddr *dst, const struct sockaddr *mask)
    {
    	switch (dst->sa_family) {
    	case AF_INET:
    		if (satosin_const(dst)->sin_addr.s_addr == INADDR_ANY &&
    		    mask && (mask->sa_len == 0 ||
    		    satosin_const(mask)->sin_addr.s_addr == INADDR_ANY))
    			if_group_egress_build();
    		break;
    #ifdef INET6
    	case AF_INET6:
    		if (IN6_ARE_ADDR_EQUAL(&(satosin6_const(dst))->sin6_addr,
    		    &in6addr_any) && mask && (mask->sa_len == 0 ||
    		    IN6_ARE_ADDR_EQUAL(&(satosin6_const(mask))->sin6_addr,
    		    &in6addr_any)))
    			if_group_egress_build();
    		break;
    #endif
    	}
    }
    
    int
    if_group_egress_build(void)
    {
    	struct ifnet		*ifp;
    	struct ifg_group	*ifg;
    	struct ifg_member	*ifgm, *next;
    	struct sockaddr_in	 sa_in;
    #ifdef INET6
    	struct sockaddr_in6	 sa_in6;
    #endif
    	struct rtentry		*rt;
    
    	TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
    		if (!strcmp(ifg->ifg_group, IFG_EGRESS))
    			break;
    
    	if (ifg != NULL)
    		TAILQ_FOREACH_SAFE(ifgm, &ifg->ifg_members, ifgm_next, next)
    			if_delgroup(ifgm->ifgm_ifp, IFG_EGRESS);
    
    	bzero(&sa_in, sizeof(sa_in));
    	sa_in.sin_len = sizeof(sa_in);
    	sa_in.sin_family = AF_INET;
    	rt = rtable_lookup(0, sintosa(&sa_in), sintosa(&sa_in), NULL, RTP_ANY);
    	while (rt != NULL) {
    		ifp = if_get(rt->rt_ifidx);
    		if (ifp != NULL) {
    			if_addgroup(ifp, IFG_EGRESS);
    			if_put(ifp);
    		}
    		rt = rtable_iterate(rt);
    	}
    
    #ifdef INET6
    	bcopy(&sa6_any, &sa_in6, sizeof(sa_in6));
    	rt = rtable_lookup(0, sin6tosa(&sa_in6), sin6tosa(&sa_in6), NULL,
    	    RTP_ANY);
    	while (rt != NULL) {
    		ifp = if_get(rt->rt_ifidx);
    		if (ifp != NULL) {
    			if_addgroup(ifp, IFG_EGRESS);
    			if_put(ifp);
    		}
    		rt = rtable_iterate(rt);
    	}
    #endif /* INET6 */
    
    	return (0);
    }
    
    /*
     * Set/clear promiscuous mode on interface ifp based on the truth value
     * of pswitch.  The calls are reference counted so that only the first
     * "on" request actually has an effect, as does the final "off" request.
     * Results are undefined if the "off" and "on" requests are not matched.
     */
    int
    ifpromisc(struct ifnet *ifp, int pswitch)
    {
    	struct ifreq ifr;
    	unsigned short oif_flags;
    	int oif_pcount, error;
    
    	NET_ASSERT_LOCKED(); /* modifying if_flags and if_pcount */
    
    	oif_flags = ifp->if_flags;
    	oif_pcount = ifp->if_pcount;
    	if (pswitch) {
    		if (ifp->if_pcount++ != 0)
    			return (0);
    		ifp->if_flags |= IFF_PROMISC;
    	} else {
    		if (--ifp->if_pcount > 0)
    			return (0);
    		ifp->if_flags &= ~IFF_PROMISC;
    	}
    
    	if ((ifp->if_flags & IFF_UP) == 0)
    		return (0);
    
    	memset(&ifr, 0, sizeof(ifr));
    	ifr.ifr_flags = ifp->if_flags;
    	error = ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
    	if (error) {
    		ifp->if_flags = oif_flags;
    		ifp->if_pcount = oif_pcount;
    	}
    
    	return (error);
    }
    
    /* Set/clear LRO flag and restart interface if needed. */
    int
    ifsetlro(struct ifnet *ifp, int on)
    {
    	struct ifreq ifrq;
    	int error = 0;
    	int s = splnet();
    	struct if_parent parent;
    
    	memset(&parent, 0, sizeof(parent));
    	if ((*ifp->if_ioctl)(ifp, SIOCGIFPARENT, (caddr_t)&parent) != -1) {
    		struct ifnet *ifp0 = if_unit(parent.ifp_parent);
    
    		if (ifp0 != NULL) {
    			ifsetlro(ifp0, on);
    			if_put(ifp0);
    		}
    	}
    
    	if (!ISSET(ifp->if_capabilities, IFCAP_LRO)) {
    		error = ENOTSUP;
    		goto out;
    	}
    
    	NET_ASSERT_LOCKED();	/* for ioctl */
    	KERNEL_ASSERT_LOCKED();	/* for if_flags */
    
    	if (on && !ISSET(ifp->if_xflags, IFXF_LRO)) {
    		if (ifp->if_type == IFT_ETHER && ether_brport_isset(ifp)) {
    			error = EBUSY;
    			goto out;
    		}
    		SET(ifp->if_xflags, IFXF_LRO);
    	} else if (!on && ISSET(ifp->if_xflags, IFXF_LRO))
    		CLR(ifp->if_xflags, IFXF_LRO);
    	else
    		goto out;
    
    	/* restart interface */
    	if (ISSET(ifp->if_flags, IFF_UP)) {
    		/* go down for a moment... */
    		CLR(ifp->if_flags, IFF_UP);
    		ifrq.ifr_flags = ifp->if_flags;
    		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
    
    		/* ... and up again */
    		SET(ifp->if_flags, IFF_UP);
    		ifrq.ifr_flags = ifp->if_flags;
    		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
    	}
     out:
    	splx(s);
    
    	return error;
    }
    
    void
    ifa_add(struct ifnet *ifp, struct ifaddr *ifa)
    {
    	NET_ASSERT_LOCKED_EXCLUSIVE();
    	TAILQ_INSERT_TAIL(&ifp->if_addrlist, ifa, ifa_list);
    }
    
    void
    ifa_del(struct ifnet *ifp, struct ifaddr *ifa)
    {
    	NET_ASSERT_LOCKED_EXCLUSIVE();
    	TAILQ_REMOVE(&ifp->if_addrlist, ifa, ifa_list);
    }
    
    void
    ifa_update_broadaddr(struct ifnet *ifp, struct ifaddr *ifa, struct sockaddr *sa)
    {
    	if (ifa->ifa_broadaddr->sa_len != sa->sa_len)
    		panic("ifa_update_broadaddr does not support dynamic length");
    	bcopy(sa, ifa->ifa_broadaddr, sa->sa_len);
    }
    
    #ifdef DDB
    /* debug function, can be called from ddb> */
    void
    ifa_print_all(void)
    {
    	struct ifnet *ifp;
    	struct ifaddr *ifa;
    
    	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
    		TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
    			char addr[INET6_ADDRSTRLEN];
    
    			switch (ifa->ifa_addr->sa_family) {
    			case AF_INET:
    				printf("%s", inet_ntop(AF_INET,
    				    &satosin(ifa->ifa_addr)->sin_addr,
    				    addr, sizeof(addr)));
    				break;
    #ifdef INET6
    			case AF_INET6:
    				printf("%s", inet_ntop(AF_INET6,
    				    &(satosin6(ifa->ifa_addr))->sin6_addr,
    				    addr, sizeof(addr)));
    				break;
    #endif
    			}
    			printf(" on %s\n", ifp->if_xname);
    		}
    	}
    }
    #endif /* DDB */
    
    void
    ifnewlladdr(struct ifnet *ifp)
    {
    #ifdef INET6
    	struct ifaddr *ifa;
    #endif
    	struct ifreq ifrq;
    	short up;
    
    	NET_ASSERT_LOCKED();	/* for ioctl and in6 */
    	KERNEL_ASSERT_LOCKED();	/* for if_flags */
    
    	up = ifp->if_flags & IFF_UP;
    
    	if (up) {
    		/* go down for a moment... */
    		ifp->if_flags &= ~IFF_UP;
    		ifrq.ifr_flags = ifp->if_flags;
    		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
    	}
    
    	ifp->if_flags |= IFF_UP;
    	ifrq.ifr_flags = ifp->if_flags;
    	(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
    
    #ifdef INET6
    	/*
    	 * Update the link-local address.  Don't do it if we're
    	 * a router to avoid confusing hosts on the network.
    	 */
    	if (!ip6_forwarding) {
    		ifa = &in6ifa_ifpforlinklocal(ifp, 0)->ia_ifa;
    		if (ifa) {
    			in6_purgeaddr(ifa);
    			if_hooks_run(&ifp->if_addrhooks);
    			in6_ifattach(ifp);
    		}
    	}
    #endif
    	if (!up) {
    		/* go back down */
    		ifp->if_flags &= ~IFF_UP;
    		ifrq.ifr_flags = ifp->if_flags;
    		(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
    	}
    }
    
    void
    if_addrhook_add(struct ifnet *ifp, struct task *t)
    {
    	mtx_enter(&if_hooks_mtx);
    	TAILQ_INSERT_TAIL(&ifp->if_addrhooks, t, t_entry);
    	mtx_leave(&if_hooks_mtx);
    }
    
    void
    if_addrhook_del(struct ifnet *ifp, struct task *t)
    {
    	mtx_enter(&if_hooks_mtx);
    	TAILQ_REMOVE(&ifp->if_addrhooks, t, t_entry);
    	mtx_leave(&if_hooks_mtx);
    }
    
    void
    if_addrhooks_run(struct ifnet *ifp)
    {
    	if_hooks_run(&ifp->if_addrhooks);
    }
    
    void
    if_rxr_init(struct if_rxring *rxr, u_int lwm, u_int hwm)
    {
    	extern int ticks;
    
    	memset(rxr, 0, sizeof(*rxr));
    
    	rxr->rxr_adjusted = ticks;
    	rxr->rxr_cwm = rxr->rxr_lwm = lwm;
    	rxr->rxr_hwm = hwm;
    }
    
    static inline void
    if_rxr_adjust_cwm(struct if_rxring *rxr)
    {
    	extern int ticks;
    
    	if (rxr->rxr_alive >= rxr->rxr_lwm)
    		return;
    	else if (rxr->rxr_cwm < rxr->rxr_hwm)
    		rxr->rxr_cwm++;
    
    	rxr->rxr_adjusted = ticks;
    }
    
    void
    if_rxr_livelocked(struct if_rxring *rxr)
    {
    	extern int ticks;
    
    	if (ticks - rxr->rxr_adjusted >= 1) {
    		if (rxr->rxr_cwm > rxr->rxr_lwm)
    			rxr->rxr_cwm--;
    
    		rxr->rxr_adjusted = ticks;
    	}
    }
    
    u_int
    if_rxr_get(struct if_rxring *rxr, u_int max)
    {
    	extern int ticks;
    	u_int diff;
    
    	if (ticks - rxr->rxr_adjusted >= 1) {
    		/* we're free to try for an adjustment */
    		if_rxr_adjust_cwm(rxr);
    	}
    
    	if (rxr->rxr_alive >= rxr->rxr_cwm)
    		return (0);
    
    	diff = min(rxr->rxr_cwm - rxr->rxr_alive, max);
    	rxr->rxr_alive += diff;
    
    	return (diff);
    }
    
    int
    if_rxr_info_ioctl(struct if_rxrinfo *uifri, u_int t, struct if_rxring_info *e)
    {
    	struct if_rxrinfo kifri;
    	int error;
    	u_int n;
    
    	error = copyin(uifri, &kifri, sizeof(kifri));
    	if (error)
    		return (error);
    
    	n = min(t, kifri.ifri_total);
    	kifri.ifri_total = t;
    
    	if (n > 0) {
    		error = copyout(e, kifri.ifri_entries, sizeof(*e) * n);
    		if (error)
    			return (error);
    	}
    
    	return (copyout(&kifri, uifri, sizeof(kifri)));
    }
    
    int
    if_rxr_ioctl(struct if_rxrinfo *ifri, const char *name, u_int size,
        struct if_rxring *rxr)
    {
    	struct if_rxring_info ifr;
    
    	memset(&ifr, 0, sizeof(ifr));
    
    	if (name != NULL)
    		strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
    
    	ifr.ifr_size = size;
    	ifr.ifr_info = *rxr;
    
    	return (if_rxr_info_ioctl(ifri, 1, &ifr));
    }
    
    /*
     * Network stack input queues.
     */
    
    void
    niq_init(struct niqueue *niq, u_int maxlen, u_int isr)
    {
    	mq_init(&niq->ni_q, maxlen, IPL_NET);
    	niq->ni_isr = isr;
    }
    
    int
    niq_enqueue(struct niqueue *niq, struct mbuf *m)
    {
    	int rv;
    
    	rv = mq_enqueue(&niq->ni_q, m);
    	if (rv == 0)
    		schednetisr(niq->ni_isr);
    	else
    		if_congestion();
    
    	return (rv);
    }
    
    int
    niq_enlist(struct niqueue *niq, struct mbuf_list *ml)
    {
    	int rv;
    
    	rv = mq_enlist(&niq->ni_q, ml);
    	if (rv == 0)
    		schednetisr(niq->ni_isr);
    	else
    		if_congestion();
    
    	return (rv);
    }
    
    __dead void
    unhandled_af(int af)
    {
    	panic("unhandled af %d", af);
    }
    
    struct taskq *
    net_tq(unsigned int ifindex)
    {
    	struct softnet *sn;
    	static int nettaskqs;
    
    	if (nettaskqs == 0)
    		nettaskqs = min(NET_TASKQ, ncpus);
    
    	sn = &softnets[ifindex % nettaskqs];
    
    	return (sn->sn_taskq);
    }
    
    void
    net_tq_barriers(const char *wmesg)
    {
    	struct task barriers[NET_TASKQ];
    	struct refcnt r = REFCNT_INITIALIZER();
    	int i;
    
    	for (i = 0; i < nitems(barriers); i++) {
    		task_set(&barriers[i], (void (*)(void *))refcnt_rele_wake, &r);
    		refcnt_take(&r);
    		task_add(softnets[i].sn_taskq, &barriers[i]);
    	}
     
    	refcnt_finalize(&r, wmesg);
    }