Network devices, or interfaces, transmit and receive data packets that are driven by the network subsystem (Corbet et al., 2005). In this chapter, we’ll examine the data structures used to manage these devices: ifnet
, ifmedia
, and mbuf
. You’ll then learn about Message Signaled Interrupts, which are an alternative to traditional interrupts and are commonly used by network devices.
To keep things simple, we’ll examine only Ethernet drivers. Also, I won’t provide a discussion on general networking concepts.
An ifnet
structure is the kernel’s representation of an individual network interface. It is defined in the <net/if_var.h>
header as follows:
struct ifnet { void *if_softc; /* Driver private data. */ void *if_l2com; /* Protocol bits. */ struct vnet *if_vnet; /* Network stack instance. */ TAILQ_ENTRY(ifnet) if_link; /* ifnet linkage. */ char if_xname[IFNAMSIZ]; /* External name. */ const char *if_dname; /* Driver name. */ int if_dunit; /* Unit number or IF_DUNIT_NONE. */ u_int if_refcount; /* Reference count. */ /* * Linked list containing every address associated with * this interface. */ struct ifaddrhead if_addrhead; int if_pcount; /* Number of promiscuous listeners. */ struct carp_if *if_carp; /* CARP interface. */ struct bpf_if *if_bpf; /* Packet filter. */ u_short if_index; /* Numeric abbreviation for interface. */ short if_timer; /* Time until if_watchdog is called. */ struct ifvlantrunk *if_vlantrunk; /* 802.1Q data. */ int if_flags; /* Flags (e.g., up, down, broadcast). */ int if_capabilities;/* Interface features and capabilities. */ int if_capenable; /* Enabled features and capabilities. */ void *if_linkmib; /* Link specific MIB data. */ size_t if_linkmiblen; /* Length of above. */ struct if_data if_data; /* Interface information. */ struct ifmultihead if_multiaddrs; /* Multicast addresses. */ int if_amcount; /* Number of multicast requests. */ /* Interface methods. */ int (*if_output) (struct ifnet *, struct mbuf *, struct sockaddr *, struct route *); void (*if_input) (struct ifnet *, struct mbuf *); void (*if_start) (struct ifnet *); int (*if_ioctl) (struct ifnet *, u_long, caddr_t); void (*if_watchdog) (struct ifnet *); void (*if_init) (void *); int (*if_resolvemulti) (struct ifnet *, struct sockaddr **, struct sockaddr *); void (*if_qflush) (struct ifnet *); int (*if_transmit) (struct ifnet *, struct mbuf *); void (*if_reassign) (struct ifnet *, struct vnet *, char *); struct vnet *if_home_vnet; /* Where we originate from. */ struct ifaddr *if_addr; /* Link level address. */ void *if_llsoftc; /* Link level softc. */ int if_drv_flags; /* Driver managed status flags. */ struct ifaltq if_snd; /* Output queue, includes altq. */ const u_int8_t *if_broadcastaddr; /* Link level broadcast addr. */ void *if_bridge; /* Bridge glue. */ struct label *if_label; /* Interface MAC label. */ /* Only used by IPv6. */ struct ifprefixhead if_prefixhead; void *if_afdata[AF_MAX]; int if_afdata_initialized; struct rwlock if_afdata_lock; struct task if_linktask; struct mtx if_addr_mtx; LIST_ENTRY(ifnet) if_clones; /* Clone interfaces. */ TAILQ_HEAD(, ifg_list) if_groups; /* Linked list of groups. */ void *if_pf_kif; /* pf(4) glue. */ void *if_lagg; /* lagg(4) glue. */ u_char if_alloctype; /* Type (e.g., Ethernet). */ /* Spare fields. */ char if_cspare[3]; /* Spare characters. */ char *if_description; /* Interface description. */ void *if_pspare[7]; /* Spare pointers. */ int if_ispare[4]; /* Spare integers. */ };
I’ll demonstrate how struct ifnet
is used in Hello, world! in Hello, world!. For now, let’s look at its method fields.
The if_init
field identifies the interface’s init routine. Init routines are called to initialize their interface.
The if_ioctl
field identifies the interface’s ioctl routine. Characteristically, ioctl routines are used to configure their interface (for example, for setting the maximum transmission unit).
The if_input
field identifies the interface’s input routine. An interface sends an interrupt whenever it receives a packet. Its driver-defined interrupt handler then calls its input routine to process the packet. Note that this is a departure from the norm. Input routines are called by a driver, while the other routines are called by the network stack. The if_input
field generally points to a link layer routine (for example, ether_input
) rather than a driver-defined routine.
Obviously, link layer routines are kernel defined. Method fields that expect a link layer routine should be defined by an *ifattach
function (such as ether_ifattach
), not directly by a driver. *ifattach
functions are described in Network Interface Structure Management Routines in Network Interface Structure Management Routines.
The if_output
field identifies the interface’s output routine. Output routines are called by the network stack to prepare an upper-layer packet for transmission. Every output routine ends by calling its interface’s transmit routine. If an interface lacks a transmit routine, its start routine is called instead. Typically, when a network driver defines a transmit routine, its start routine is undefined, and vice versa. The if_output
field generally points to a link layer routine (for example, ether_output
) rather than a driver-defined routine.
The if_start
field identifies the interface’s start routine. Before I describe start routines, it’s important to discuss send queues. Send queues are filled by output routines. Start routines remove one packet from their send queue and deposit it in their interface’s transmit ring. They repeat this process until the send queue is empty or the transmit ring is full. Transmit rings are simply ring buffers used for transmission. Network interfaces use ring buffers for transmission and reception.
The if_transmit
field identifies the interface’s transmit routine. Transmit routines are an alternative to start routines. Transmit routines maintain their own send queues. That is, they forego the predefined send queue, and output routines push packets directly to them. Transmit routines can maintain multiple send queues, which makes them ideal for interfaces with multiple transmit rings.
The if_qflush
field identifies the interface’s qflush routine. Qflush routines are called to flush the send queues of transmit routines. Every transmit routine must have a corresponding qflush routine.
The if_resolvemulti
field identifies the interface’s resolvemulti routine. Resolvemulti routines are called to resolve a network layer address into a link layer address when registering a multicast address with their interface. The if_resolvemulti
field generally points to a link layer routine (for example, ether_resolvemulti
) rather than a driver-defined routine.
The if_reassign
field identifies the interface’s reassign routine. Reassign routines are called before their interface is moved to another virtual network stack (vnet). They perform any tasks necessary before the move. The if_reassign
field generally points to a link layer routine (for example, ether_reassign
) rather than a driver-defined routine.
The if_watchdog
field is deprecated and must not be defined. In FreeBSD version 9, if_watchdog
will be removed.
3.146.65.212