src/sys/net/if.c

3592 lines
75 KiB
C

/* $OpenBSD: if.c,v 1.718 2024/02/06 00:18:53 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();
if (panicstr == NULL) {
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);
}