STRUCT NET_DEVICE(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

πŸ’… struct net_device(9)

okay bestie, welcome to the Kernel API Manual ✨ β€” no cap, you're gonna want to sit down for this one. We're talking about struct net_device, and this is Part 1 of 4, so hydrate before you scroll, because there's more coming 🫢


πŸ¦‹ NAME πŸ¦‹

struct net_device β€” literally the DEVICE structure. that's it. that's the whole vibe. it's the struct, it's the device, it's giving main character energy in the networking stack πŸ‘‘


more parts incoming β€” don't go anywhere, bestie πŸ’–

✨ SYNOPSIS ✨

okay bestie, deep breath. struct net_device is the main character struct of the whole networking stack β€” this is the thing your NIC driver fills in, this is the thing every ip link command is secretly poking at β€” and she is long. like, "scrolling for twenty minutes" long. so we're doing this in chunks, and every single field is coming with us. no field left behind. periodt. πŸ’…

quick legend before we dive: those #ifdef / #endif and IS_ENABLED(...) wrappers mean the field only exists in the compiled struct when that kernel config is turned on. so depending on your build, some of these cuties just aren't there. 🫢

    struct net_device {
        ;
        struct {
          unsigned long           priv_flags:32;
          unsigned long           lltx:1;
          unsigned long           netmem_tx:2;
        };
        const struct net_device_ops *netdev_ops;
        const struct header_ops *header_ops;
        struct netdev_queue     *_tx;
        netdev_features_t gso_partial_features;
        unsigned int            real_num_tx_queues;
        unsigned int            gso_max_size;
        unsigned int            gso_ipv4_max_size;
        u16 gso_max_segs;
        s16 num_tc;
        unsigned int            mtu;
        unsigned short          needed_headroom;
        struct netdev_tc_txq    tc_to_txq[TC_MAX_QUEUE];
      #ifdef CONFIG_XPS;
        struct xps_dev_maps *xps_maps[XPS_MAPS_MAX];
      #endif;
      #ifdef CONFIG_NETFILTER_EGRESS;
        struct nf_hook_entries *nf_hooks_egress;
      #endif;
      #ifdef CONFIG_NET_XGRESS;
        struct bpf_mprog_entry *tcx_egress;
      #endif;
        ;
        ;

right off the bat we've got an anonymous struct of bitfields β€” priv_flags:32, lltx:1, netmem_tx:2 β€” squished into unsigned longs because our girl loves a compact layout. πŸŽ€

then the transmit side of the family: netdev_ops and header_ops (the big callback tables πŸ‘‘), _tx, real_num_tx_queues, the gso_* trio and gso_partial_features, num_tc with tc_to_txq[TC_MAX_QUEUE], plus mtu and needed_headroom. then alll the conditional egress bits: xps_maps[XPS_MAPS_MAX] when CONFIG_XPS is on, nf_hooks_egress for CONFIG_NETFILTER_EGRESS, and tcx_egress for CONFIG_NET_XGRESS. and then two lonely ; lines. no thoughts, head empty. πŸ™ˆ


next chunk, and we're switching to the receive side. the vibe check: sims, states, and features.

        union {
          struct pcpu_lstats __percpu             *lstats;
          struct pcpu_sw_netstats __percpu        *tstats;
          struct pcpu_dstats __percpu             *dstats;
        };
        unsigned long           state;
        unsigned int            flags;
        unsigned short          hard_header_len;
        enum netdev_stat_type   pcpu_stat_type:8;
        netdev_features_t features;
        struct inet6_dev *ip6_ptr;
        ;
        ;
        struct bpf_prog *xdp_prog;
        struct list_head        ptype_specific;
        int ifindex;
        unsigned int            real_num_rx_queues;
        struct netdev_rx_queue  *_rx;
        unsigned int            gro_max_size;
        unsigned int            gro_ipv4_max_size;
        rx_handler_func_t *rx_handler;
        void *rx_handler_data;
        possible_net_t nd_net;
      #ifdef CONFIG_NETPOLL;
        struct netpoll_info *npinfo;
      #endif;
      #ifdef CONFIG_NET_XGRESS;
        struct bpf_mprog_entry *tcx_ingress;
      #endif;
        ;

ok so the union is giving "pick your per-CPU stat flavour": lstats, tstats, or dstats, and pcpu_stat_type:8 is the 8-bit enum telling everyone which one you picked. iconic. ✨

then state, flags, hard_header_len, features, ip6_ptr, and pcpu_stat_type. and yes β€” two more lil ; lines just vibing. then xdp_prog, ptype_specific, ifindex (the number you see in ip link, ily), real_num_rx_queues, _rx, gro_max_size, gro_ipv4_max_size, rx_handler / rx_handler_data, and nd_net. conditional girlies: npinfo under CONFIG_NETPOLL and tcx_ingress under CONFIG_NET_XGRESS. πŸ’–


πŸ¦‹ the identity era

names, addresses, and the whole friend-group of lists:

        char name[IFNAMSIZ];
        struct netdev_name_node *name_node;
        struct dev_ifalias      *ifalias;
        unsigned long           mem_end;
        unsigned long           mem_start;
        unsigned long           base_addr;
        struct list_head        dev_list;
        struct list_head        napi_list;
        struct list_head        unreg_list;
      #ifdef CONFIG_DEBUG_NET_SMALL_RTNL;
        struct list_head        unreg_list_net;
      #endif;
        struct list_head        close_list;
        struct list_head        ptype_all;
        struct {
          struct list_head upper;
          struct list_head lower;
        } adj_list;
        xdp_features_t xdp_features;
        const struct xdp_metadata_ops *xdp_metadata_ops;
        const struct xsk_tx_metadata_ops *xsk_tx_metadata_ops;
        unsigned short          gflags;
        unsigned short          needed_tailroom;
        netdev_features_t hw_features;
        netdev_features_t wanted_features;
        netdev_features_t vlan_features;
        netdev_features_t hw_enc_features;
        netdev_features_t mpls_features;
        netdev_features_t mangleid_features;
        unsigned int            min_mtu;
        unsigned int            max_mtu;
        unsigned short          type;
        unsigned char           min_header_len;
        unsigned char           name_assign_type;
        int group;
        struct net_device_stats stats;
        struct net_device_core_stats __percpu *core_stats;
        atomic_t carrier_up_count;
        atomic_t carrier_down_count;

name[IFNAMSIZ] β€” the actual name, capital-I Iconic. her nickname lives in name_node, her alias in ifalias. then the memory trio mem_end / mem_start / base_addr, and the list-head gang: dev_list, napi_list, unreg_list (plus unreg_list_net when CONFIG_DEBUG_NET_SMALL_RTNL is on), close_list, ptype_all, and that cute anonymous struct with upper / lower living inside adj_list. 🫢

features, features, features: hw_features, wanted_features, vlan_features, hw_enc_features, mpls_features, mangleid_features. not me listing them like a prayer. 😭 and min_mtu / max_mtu bracketing your mtu. plus type, min_header_len, name_assign_type, group, the classic stats and per-CPU core_stats, and the two atomics counting carrier_up_count / carrier_down_count.


⭐ ops o'clock

every optional callback table in the building, plus the addressing-era fields:

      #ifdef CONFIG_WIRELESS_EXT;
        const struct iw_handler_def *wireless_handlers;
      #endif;
        const struct ethtool_ops *ethtool_ops;
      #ifdef CONFIG_NET_L3_MASTER_DEV;
        const struct l3mdev_ops *l3mdev_ops;
      #endif;
      #if IS_ENABLED(CONFIG_IPV6);
        const struct ndisc_ops *ndisc_ops;
      #endif;
      #ifdef CONFIG_XFRM_OFFLOAD;
        const struct xfrmdev_ops *xfrmdev_ops;
      #endif;
      #if IS_ENABLED(CONFIG_TLS_DEVICE);
        const struct tlsdev_ops *tlsdev_ops;
      #endif;
        unsigned int            operstate;
        unsigned char           link_mode;
        unsigned char           if_port;
        unsigned char           dma;
        unsigned char           perm_addr[MAX_ADDR_LEN];
        unsigned char           addr_assign_type;
        unsigned char           addr_len;
        unsigned char           upper_level;
        unsigned char           lower_level;
        u8 threaded;
        unsigned short          neigh_priv_len;
        unsigned short          dev_id;
        unsigned short          dev_port;
        int irq;
        u32 priv_len;
        spinlock_t addr_list_lock;
        struct netdev_hw_addr_list      uc;
        struct netdev_hw_addr_list      mc;
        struct netdev_hw_addr_list      dev_addrs;
      #ifdef CONFIG_SYSFS;
        struct kset             *queues_kset;
      #endif;
      #ifdef CONFIG_LOCKDEP;
        struct list_head        unlink_list;
      #endif;
        unsigned int            promiscuity;
        unsigned int            allmulti;
        bool uc_promisc;
        struct list_head        work_node;
        netdevice_tracker work_tracker;
        unsigned long           work_pending;
        unsigned long           work_core_pending;
        struct netdev_hw_addr_list      rx_mode_addr_cache;
        struct timer_list       rx_mode_retry_timer;
        unsigned int            rx_mode_retry_count;
      #ifdef CONFIG_LOCKDEP;
        unsigned char           nested_level;
      #endif;

ethtool_ops is the only unconditional one in that ops parade β€” everybody else is fenced off: wireless_handlers (CONFIG_WIRELESS_EXT), l3mdev_ops (CONFIG_NET_L3_MASTER_DEV), ndisc_ops (IS_ENABLED(CONFIG_IPV6)), xfrmdev_ops (CONFIG_XFRM_OFFLOAD), tlsdev_ops (IS_ENABLED(CONFIG_TLS_DEVICE)). conditional queens only. πŸ‘‘

then the address era: operstate, link_mode, if_port, dma, perm_addr[MAX_ADDR_LEN], addr_assign_type, addr_len, upper_level, lower_level, threaded, neigh_priv_len, dev_id, dev_port, irq, priv_len, and addr_list_lock guarding the three address lists uc, mc, dev_addrs. queues_kset shows up under CONFIG_SYSFS, unlink_list under CONFIG_LOCKDEP.

then the promisc / rx-mode corner: promiscuity, allmulti, uc_promisc, plus work_node, work_tracker, work_pending, work_core_pending, rx_mode_addr_cache, rx_mode_retry_timer, rx_mode_retry_count β€” and nested_level if CONFIG_LOCKDEP said yes. πŸ’€ this is a lot, bestie, but we're locked in.


🌈 protocol pointers & the queue counts

this is where every subsystem gets a lil guest slot in the struct:

        struct in_device *ip_ptr;
        struct hlist_head       fib_nh_head;
      #if IS_ENABLED(CONFIG_VLAN_8021Q);
        struct vlan_info *vlan_info;
      #endif;
      #if IS_ENABLED(CONFIG_NET_DSA);
        struct dsa_port         *dsa_ptr;
      #endif;
      #if IS_ENABLED(CONFIG_TIPC);
        struct tipc_bearer *tipc_ptr;
      #endif;
      #if IS_ENABLED(CONFIG_CFG80211);
        struct wireless_dev     *ieee80211_ptr;
      #endif;
      #if IS_ENABLED(CONFIG_IEEE802154) || IS_ENABLED(CONFIG_6LOWPAN);
        struct wpan_dev         *ieee802154_ptr;
      #endif;
      #if IS_ENABLED(CONFIG_MPLS_ROUTING);
        struct mpls_dev *mpls_ptr;
      #endif;
      #if IS_ENABLED(CONFIG_MCTP);
        struct mctp_dev *mctp_ptr;
      #endif;
      #if IS_ENABLED(CONFIG_INET_PSP);
        struct psp_dev *psp_dev;
      #endif;
        const unsigned char     *dev_addr;
        unsigned int            num_rx_queues;
      #define GRO_LEGACY_MAX_SIZE     65536u;
      #define GRO_MAX_SIZE            (8 * 65535u);
        unsigned int            xdp_zc_max_segs;
        struct netdev_queue *ingress_queue;
      #ifdef CONFIG_NETFILTER_INGRESS;
        struct nf_hook_entries *nf_hooks_ingress;
      #endif;
        unsigned char           broadcast[MAX_ADDR_LEN];
      #ifdef CONFIG_RFS_ACCEL;
        struct cpu_rmap         *rx_cpu_rmap;
      #endif;
        struct hlist_node       index_hlist;
        unsigned int            num_tx_queues;
        struct Qdisc *qdisc;
        unsigned int            tx_queue_len;
        spinlock_t tx_global_lock;
        struct xdp_dev_bulk_queue __percpu *xdp_bulkq;
      #ifdef CONFIG_NET_SCHED;
        unsigned long qdisc_hash[1 << (( 4) - 1)];
      #endif;
        struct timer_list       watchdog_timer;
        int watchdog_timeo;
        spinlock_t watchdog_lock;
        bool watchdog_ref_held;
        u32 proto_down_reason;
        struct list_head        todo_list;
      #ifdef CONFIG_PCPU_DEV_REFCNT;
        int __percpu            *pcpu_refcnt;
      #else;
        refcount_t dev_refcnt;
      #endif;
        struct ref_tracker_dir  refcnt_tracker;
        struct list_head        link_watch_list;
        u8 reg_state;
        bool dismantle;
        bool moving_ns;
        bool rtnl_link_initializing;
        bool needs_free_netdev;
        void (*priv_destructor)(struct net_device *dev);
        void *ml_priv;
        enum netdev_ml_priv_type        ml_priv_type;
      #if IS_ENABLED(CONFIG_GARP);
        struct garp_port *garp_port;
      #endif;
      #if IS_ENABLED(CONFIG_MRP);
        struct mrp_port *mrp_port;
      #endif;
      #if IS_ENABLED(CONFIG_NET_DROP_MONITOR);
        struct dm_hw_stat_delta *dm_private;
      #endif;

ip_ptr and fib_nh_head are unconditional; then the guest list: vlan_info, dsa_ptr, tipc_ptr, ieee80211_ptr, ieee802154_ptr (on CONFIG_IEEE802154 or CONFIG_6LOWPAN β€” either one gets her in πŸŽ€), mpls_ptr, mctp_ptr, psp_dev.

then constants time, and these are literal, copy them exactly:

  • GRO_LEGACY_MAX_SIZE is 65536u
  • GRO_MAX_SIZE is (8 * 65535u)

followed by xdp_zc_max_segs, ingress_queue, nf_hooks_ingress (under CONFIG_NETFILTER_INGRESS), broadcast[MAX_ADDR_LEN], rx_cpu_rmap (under CONFIG_RFS_ACCEL), index_hlist, num_tx_queues, qdisc, tx_queue_len, tx_global_lock, xdp_bulkq, and under CONFIG_NET_SCHED the qdisc_hash[1 << (( 4) - 1)] array. (yes that double space inside (( 4) is in the original, we do not touch it, we respect her.)

the watchdog trio-turned-quartet: watchdog_timer, watchdog_timeo, watchdog_lock, watchdog_ref_held. also proto_down_reason and todo_list.

refcount representation is a whole #ifdef/#else situation, so look closely:

      #ifdef CONFIG_PCPU_DEV_REFCNT;
        int __percpu            *pcpu_refcnt;
      #else;
        refcount_t dev_refcnt;
      #endif;

then refcnt_tracker, link_watch_list, and the registration-state bbs: reg_state, dismantle, moving_ns, rtnl_link_initializing, needs_free_netdev, priv_destructor, ml_priv, ml_priv_type. and more conditional besties: garp_port, mrp_port, dm_private. 🌸


🧚 the grand finale of this part

struct device, sysfs groups, more constants, and a truly unhinged number of bools:

        struct device           dev;
        const struct attribute_group *sysfs_groups[5];
        const struct attribute_group *sysfs_rx_queue_group;
        const struct rtnl_link_ops *rtnl_link_ops;
        const struct netdev_stat_ops *stat_ops;
        const struct netdev_queue_mgmt_ops *queue_mgmt_ops;
      #define GSO_MAX_SEGS            65535u;
      #define GSO_LEGACY_MAX_SIZE     65536u;
      #define GSO_MAX_SIZE            (8 * GSO_MAX_SEGS);
      #define TSO_LEGACY_MAX_SIZE     65536;
      #define TSO_MAX_SIZE            UINT_MAX;
        unsigned int            tso_max_size;
      #define TSO_MAX_SEGS            U16_MAX;
        u16 tso_max_segs;
      #ifdef CONFIG_DCB;
        const struct dcbnl_rtnl_ops *dcbnl_ops;
      #endif;
        u8 prio_tc_map[TC_BITMASK + 1];
      #if IS_ENABLED(CONFIG_FCOE);
        unsigned int            fcoe_ddp_xid;
      #endif;
      #if IS_ENABLED(CONFIG_CGROUP_NET_PRIO);
        struct netprio_map *priomap;
      #endif;
        struct phy_link_topology        *link_topo;
        struct phy_device       *phydev;
        struct sfp_bus          *sfp_bus;
        struct lock_class_key   *qdisc_tx_busylock;
        bool proto_down;
        bool irq_affinity_auto;
        bool rx_cpu_rmap_auto;
        unsigned long           see_all_hwtstamp_requests:1;
        unsigned long           change_proto_down:1;
        unsigned long           netns_immutable:1;
        unsigned long           fcoe_mtu:1;
        struct list_head        net_notifier_list;
      #if IS_ENABLED(CONFIG_MACSEC);
        const struct macsec_ops *macsec_ops;
      #endif;
        const struct udp_tunnel_nic_info        *udp_tunnel_nic_info;
        struct udp_tunnel_nic   *udp_tunnel_nic;
        struct netdev_config    *cfg;
        struct netdev_config    *cfg_pending;
        struct ethtool_netdev_state *ethtool;
        struct bpf_xdp_entity   xdp_state[__MAX_XDP_MODE];
        u8 dev_addr_shadow[MAX_ADDR_LEN];
        netdevice_tracker linkwatch_dev_tracker;
        netdevice_tracker watchdog_dev_tracker;
        netdevice_tracker dev_registered_tracker;
        struct rtnl_hw_stats64  *offload_xstats_l3;
        struct devlink_port     *devlink_port;
      #if IS_ENABLED(CONFIG_DPLL);
        struct dpll_pin *dpll_pin;
      #endif;
      #if IS_ENABLED(CONFIG_PAGE_POOL);
        struct hlist_head       page_pools;
      #endif;
        struct dim_irq_moder    *irq_moder;
        u64 max_pacing_offload_horizon;
        struct napi_config      *napi_config;
        u32 num_napi_configs;
        u32 napi_defer_hard_irqs;
        unsigned long           gro_flush_timeout;
        bool up;
        bool request_ops_lock;

okay the macro section deserves a lil scroll of honour, all exact:

  • GSO_MAX_SEGS = 65535u
  • GSO_LEGACY_MAX_SIZE = 65536u
  • GSO_MAX_SIZE = (8 * GSO_MAX_SEGS)
  • TSO_LEGACY_MAX_SIZE = 65536
  • TSO_MAX_SIZE = UINT_MAX
  • TSO_MAX_SEGS = U16_MAX

with tso_max_size and tso_max_segs as the actual fields sitting between them. dcbnl_ops needs CONFIG_DCB, prio_tc_map[TC_BITMASK + 1] is just there being a map, fcoe_ddp_xid needs IS_ENABLED(CONFIG_FCOE), priomap needs IS_ENABLED(CONFIG_CGROUP_NET_PRIO).

then physical-layer besties link_topo, phydev, sfp_bus, qdisc_tx_busylock, and the flags: proto_down, irq_affinity_auto, rx_cpu_rmap_auto, plus the :1 bitfields see_all_hwtstamp_requests, change_proto_down, netns_immutable, fcoe_mtu, and net_notifier_list. πŸ¦‹

macsec_ops needs IS_ENABLED(CONFIG_MACSEC). then udp_tunnel_nic_info / udp_tunnel_nic, cfg / cfg_pending, ethtool, xdp_state[__MAX_XDP_MODE], dev_addr_shadow[MAX_ADDR_LEN], three netdevice_trackers (linkwatch_dev_tracker, watchdog_dev_tracker, dev_registered_tracker), offload_xstats_l3, devlink_port, then dpll_pin (CONFIG_DPLL) and page_pools (CONFIG_PAGE_POOL).

and we land on: irq_moder, max_pacing_offload_horizon, napi_config, num_napi_configs, napi_defer_hard_irqs, gro_flush_timeout, up, request_ops_lock.

and that's where this chunk of the struct stops β€” she keeps going, so don't you dare close the brace yet, bestie. πŸ’œ

no cap, we covered every single field in this stretch, conditionals and macros and all. ate and left no crumbs. πŸ”₯

…and the struct itself wraps up with these last few fields, bestie β€” copy them exactly, no cap:

           struct mutex            lock;
         #if IS_ENABLED(CONFIG_NET_SHAPER);
           struct net_shaper_hierarchy *net_shaper_hierarchy;
         #endif;
           struct hlist_head neighbours[NEIGH_NR_TABLES];
           struct hwtstamp_provider *hwprov;
           u8 priv[];
        };

🫢 Members 🫢

Okay so this is the member list and it is long β€” we're going through every single one, no skipping, periodt. πŸ’…

  • {unnamed_struct} β€” anonymous πŸ™ˆ that's the whole description, bestie.
  • priv_flags β€” flags invisible to userspace, defined as bits; go look at enum netdev_priv_flags for the actual definitions πŸ‘€
  • lltx β€” device supports lockless Tx. Deprecated for real HW drivers β€” mainly used by logical interfaces, like bonding and tunnels.
  • netmem_tx β€” device netmem TX mode ✨
  • netdev_ops β€” includes several pointers to callbacks, for when you wanna override the ndo_*() functions.
  • header_ops β€” includes callbacks for creating, parsing, caching, etc. of Layer 2 headers πŸŽ€
  • _tx β€” array of TX queues.
  • gso_partial_features β€” value(s) from NETIF_F_GSO.
  • real_num_tx_queues β€” number of TX queues currently active in device.
  • gso_max_size β€” maximum size of generic segmentation offload.
  • gso_ipv4_max_size β€” maximum size of generic segmentation offload, for IPv4.
  • gso_max_segs β€” maximum number of segments that can be passed to the NIC for GSO.
  • num_tc β€” number of traffic classes in the net device.
  • mtu β€” interface MTU value.
  • needed_headroom β€” extra headroom the hardware may need β€” but not in all cases can this be guaranteed, so don't come for me 😭
  • tc_to_txq β€” XXX: need comments on this one (yes really, that's the official description).
  • xps_maps β€” XXX: need comments on this one too, bestie.
  • nf_hooks_egress β€” netfilter hooks executed for egress packets.
  • tcx_egress β€” BPF & clsact qdisc specific data for egress processing.
  • {unnamed_union} β€” anonymous again 🫠
  • lstats β€” loopback statistics: packets, bytes.
  • tstats β€” tunnel statistics: RX/TX packets, RX/TX bytes.
  • dstats β€” dummy statistics: RX/TX/drop packets, RX/TX bytes.
  • state β€” generic network queuing layer state, see netdev_state_t.
  • flags β€” interface flags (a la BSD).
  • hard_header_len β€” maximum hardware header length.
  • pcpu_stat_type β€” type of device statistics which the core should allocate/free: none, lstats, tstats, dstats. none means the driver is handling statistics allocation/freeing internally.
  • features β€” currently active device features.
  • ip6_ptr β€” IPv6 specific data.
  • xdp_prog β€” XDP sockets filter program pointer.
  • ptype_specific β€” device-specific, protocol-specific packet handlers.
  • ifindex β€” interface index πŸ”’
  • real_num_rx_queues β€” number of RX queues currently active in device.
  • _rx β€” array of RX queues.
  • gro_max_size β€” maximum size of aggregated packet in generic receive offload (GRO).
  • gro_ipv4_max_size β€” maximum size of aggregated packet in generic receive offload (GRO), for IPv4.
  • rx_handler β€” handler for received packets.
  • rx_handler_data β€” XXX: need comments on this one. iconic of them honestly.
  • nd_net β€” network namespace this network device is inside, protected by lock πŸ”’
  • npinfo β€” XXX: need comments on this one.
  • tcx_ingress β€” BPF & clsact qdisc specific data for ingress processing.
  • name β€” this is the first field of the "visible" part of this structure (i.e. as seen by users in the "Space.c" file). It is the name of the interface 🏷️
  • name_node β€” name hashlist node.
  • ifalias β€” SNMP alias.
  • mem_end β€” shared memory end.
  • mem_start β€” shared memory start.
  • base_addr β€” device I/O address.
  • dev_list β€” the global list of network devices.
  • napi_list β€” list entry used for polling NAPI devices.
  • unreg_list β€” list entry when we are unregistering the device; see the function unregister_netdev.
  • unreg_list_net β€” list entry when we are unregistering the cross-netns device; see the function unregister_netdevice_queue_net.
  • close_list β€” list entry used when we are closing the device.
  • ptype_all β€” device-specific packet handlers for all protocols.
  • adj_list β€” directly linked devices, like slaves for bonding ⛓️
  • xdp_features β€” XDP capability supported by the device.
  • xdp_metadata_ops β€” includes pointers to XDP metadata callbacks.
  • xsk_tx_metadata_ops β€” includes pointers to AF_XDP TX metadata callbacks.
  • gflags β€” global flags (kept as legacy) 🫢
  • needed_tailroom β€” extra tailroom the hardware may need, but not in all cases can this be guaranteed. Some cases also use LL_MAX_HEADER instead to allocate the skb.
  • hw_features β€” user-changeable features.
  • wanted_features β€” user-requested features.
  • vlan_features β€” mask of features inheritable by VLAN devices.
  • hw_enc_features β€” mask of features inherited by encapsulating devices. This field indicates what encapsulation offloads the hardware is capable of doing, and drivers will need to set them appropriately πŸ‘‘
  • mpls_features β€” mask of features inheritable by MPLS.
  • mangleid_features β€” mask of features requiring MANGLEID, will be disabled together with the latter.
  • min_mtu β€” interface minimum MTU value.
  • max_mtu β€” interface maximum MTU value.
  • type β€” interface hardware type.
  • min_header_len β€” minimum hardware header length.
  • name_assign_type β€” network interface name assignment type.
  • group β€” the group the device belongs to.
  • stats β€” statistics struct, which was left as a legacy β€” use rtnl_link_stats64 instead.
  • core_stats β€” core networking counters, do not use this in drivers.
  • carrier_up_count β€” number of times the carrier has been up ⬆️
  • carrier_down_count β€” number of times the carrier has been down ⬇️
  • wireless_handlers β€” list of functions to handle Wireless Extensions, instead of ioctl; see <net/iw_handler.h> for details.
  • ethtool_ops β€” management operations.
  • l3mdev_ops β€” Layer 3 master device operations.
  • ndisc_ops β€” includes callbacks for different IPv6 neighbour discovery handling. Necessary for e.g. 6LoWPAN πŸ¦‹
  • xfrmdev_ops β€” transformation offload operations.
  • tlsdev_ops β€” Transport Layer Security offload operations.
  • operstate β€” RFC2863 operstate.
  • link_mode β€” mapping policy to operstate.
  • if_port β€” selectable AUI, TP, ...
  • dma β€” DMA channel.
  • perm_addr β€” permanent hw address.
  • addr_assign_type β€” hw address assignment type.
  • addr_len β€” hardware address length.
  • upper_level β€” maximum depth level of upper devices.
  • lower_level β€” maximum depth level of lower devices.
  • threaded β€” napi threaded state.
  • neigh_priv_len β€” used in neigh_alloc.
  • dev_id β€” used to differentiate devices that share the same link layer address.
  • dev_port β€” used to differentiate devices that share the same function.
  • irq β€” device IRQ number ⚑
  • priv_len β€” size of the ->priv flexible array.
  • addr_list_lock β€” XXX: need comments on this one.
  • uc β€” unicast mac addresses.
  • mc β€” multicast mac addresses πŸ“‘
  • dev_addrs β€” list of device hw addresses.
  • queues_kset β€” group of all Kobjects in the Tx and RX queues.
  • unlink_list β€” as netif_addr_lock can be called recursively, keep a list of interfaces to be deleted.
  • promiscuity β€” number of times the NIC is told to work in promiscuous mode; if it becomes 0 the NIC will exit promiscuous mode πŸ‘€
  • allmulti β€” counter, enables or disables allmulticast mode.
  • uc_promisc β€” counter that indicates promiscuous mode has been enabled due to the need to listen to additional unicast addresses in a device that does not implement ndo_set_rx_mode.
  • work_node β€” list entry for async netdev_work processing.
  • work_tracker β€” refcount tracker for async netdev_work.
  • work_pending β€” driver-defined pending netdev_work, passed to ndo_work (see netdev_work_sched).
  • work_core_pending β€” core-defined pending netdev_work (NETDEV_WORK_*).
  • rx_mode_addr_cache β€” recycled snapshot entries for rx_mode work.
  • rx_mode_retry_timer β€” timer that re-queues rx_mode work after failure ⏰
  • rx_mode_retry_count β€” number of consecutive retries already scheduled.
  • nested_level β€” used as a parameter of spin_lock_nested of dev->addr_list_lock.
  • ip_ptr β€” IPv4 specific data.
  • fib_nh_head β€” nexthops associated with this netdev.
  • vlan_info β€” VLAN info.
  • dsa_ptr β€” dsa specific data.
  • tipc_ptr β€” TIPC specific data.
  • ieee80211_ptr β€” IEEE 802.11 specific data, assign before registering πŸ“Ά
  • ieee802154_ptr β€” IEEE 802.15.4 low-rate Wireless Personal Area Network device struct.
  • mpls_ptr β€” mpls_dev struct pointer.
  • mctp_ptr β€” MCTP specific data.
  • psp_dev β€” PSP crypto device registered for this netdev πŸ”
  • dev_addr β€” hw address (before bcast, because most packets are unicast).
  • num_rx_queues β€” number of RX queues allocated at register_netdev time.
  • xdp_zc_max_segs β€” maximum number of segments supported by AF_XDP zero copy driver.
  • ingress_queue β€” XXX: need comments on this one.
  • nf_hooks_ingress β€” netfilter hooks executed for ingress packets.
  • broadcast β€” hw bcast address πŸ“£
  • rx_cpu_rmap β€” CPU reverse-mapping for RX completion interrupts, indexed by RX queue number. Assigned by driver. This must only be set if the ndo_rx_flow_steer operation is defined.
  • index_hlist β€” device index hash chain.
  • num_tx_queues β€” number of TX queues allocated at alloc_netdev_mq time.
  • qdisc β€” root qdisc from userspace point of view.
  • tx_queue_len β€” max frames per queue allowed.
  • tx_global_lock β€” XXX: need comments on this one.
  • xdp_bulkq β€” XDP device bulk queue.
  • qdisc_hash β€” qdisc hash table.
  • watchdog_timer β€” list of timers.
  • watchdog_timeo β€” represents the timeout that is used by the watchdog (see dev_watchdog).
  • watchdog_lock β€” protect watchdog_ref_held.
  • watchdog_ref_held β€” true if the watchdog device ref is taken.
  • proto_down_reason β€” reason a netdev interface is held down.
  • todo_list β€” delayed register/unregister.
  • pcpu_refcnt β€” number of references to this device.
  • dev_refcnt β€” number of references to this device. (yes, two of them, they said what they said πŸ’€)

okay bestie we're still in the members section, she's long, she's iconic, she contains multitudes πŸ’… buckle up, here's the rest:

  • refcnt_tracker β€” the tracker directory for tracked references to this device πŸŽ€
  • link_watch_list β€” XXX: need comments on this one πŸ™ˆ literally the kernel devs left us on read
  • reg_state β€” the register/unregister state machine, she runs the whole getting-registered pipeline ✨
  • dismantle β€” this device is about to be freed, so like... say your goodbyes 😭
  • moving_ns β€” the device is changing netns, and it's protected by lock πŸ”’
  • rtnl_link_initializing β€” device is being created, so pls suppress events while she's having her moment πŸ’…
  • needs_free_netdev β€” should unregister perform free_netdev? that's the whole question 🫢
  • priv_destructor β€” called from unregister
  • ml_priv β€” mid-layer private (the mid layer's lil secret 🀫)
  • ml_priv_type β€” mid-layer private type, the label on the secret
  • garp_port β€” GARP πŸ¦‹
  • mrp_port β€” MRP 🌸
  • dm_private β€” drop monitor private
  • dev β€” the class/net/name entry, her whole identity lowkey πŸ‘‘
  • sysfs_groups β€” space for optional device, statistics and wireless sysfs groups
  • sysfs_rx_queue_group β€” space for optional per-rx queue attributes
  • rtnl_link_ops β€” Rtnl_link_ops
  • stat_ops β€” optional ops for queue-aware statistics πŸ“Š
  • queue_mgmt_ops β€” optional ops for queue management
  • tso_max_size β€” device (as in HW) limit on the max TSO request size
  • tso_max_segs β€” device (as in HW) limit on the max TSO segment count
  • dcbnl_ops β€” Data Center Bridging netlink ops
  • prio_tc_map β€” XXX: need comments on this one (again!! the lore is missing 😩)
  • fcoe_ddp_xid β€” max exchange id for FCoE LRO by ddp
  • priomap β€” XXX: need comments on this one
  • link_topo β€” physical link topology tracking attached PHYs
  • phydev β€” physical device may attach itself for hardware timestamping ⏱️
  • sfp_bus β€” attached struct sfp_bus structure.
  • qdisc_tx_busylock β€” lockdep class annotating Qdisc->busylock spinlock
  • proto_down β€” protocol port state information can be sent to the switch driver and used to set the phys state of the switch port.
  • irq_affinity_auto β€” driver wants the core to store and re-assign the IRQ affinity. Set by netif_enable_irq_affinity, then the driver must create a persistent napi by netif_napi_add_config and finally bind the napi to IRQ (via netif_napi_set_irq). slay, that's a whole three-step routine πŸ’ƒ
  • rx_cpu_rmap_auto β€” driver wants the core to manage the ARFS rmap. Set by calling netif_enable_cpu_rmap.
  • see_all_hwtstamp_requests β€” device wants to see calls to ndo_hwtstamp_set for all timestamp requests regardless of source, even if those aren't HWTSTAMP_SOURCE_NETDEV πŸ‘€
  • change_proto_down β€” device supports setting carrier via IFLA_PROTO_DOWN
  • netns_immutable β€” interface can't change network namespaces, she's staying right here 🚫
  • fcoe_mtu β€” device supports maximum FCoE MTU, 2158 bytes
  • net_notifier_list β€” list of per-net netdev notifier block that follow this device when it is moved to another network namespace.
  • macsec_ops β€” MACsec offloading ops
  • udp_tunnel_nic_info β€” static structure describing the UDP tunnel offload capabilities of the device
  • udp_tunnel_nic β€” UDP tunnel offload state
  • cfg β€” net_device queue-related configuration
  • cfg_pending β€” same as cfg but when device is being actively reconfigured; includes any changes to the configuration requested by the user, but which may or may not be rejected. suspense?? πŸ™ˆ
  • ethtool β€” ethtool related state
  • xdp_state β€” stores info on attached XDP BPF programs
  • dev_addr_shadow β€” copy of dev_addr to catch direct writes πŸ‘―
  • linkwatch_dev_tracker β€” refcount tracker used by linkwatch.
  • watchdog_dev_tracker β€” refcount tracker used by watchdog. πŸ•
  • dev_registered_tracker β€” tracker for reference held while registered
  • offload_xstats_l3 β€” L3 HW stats for this netdevice.
  • devlink_port β€” pointer to related devlink port structure. Assigned by a driver before netdev registration using SET_NETDEV_DEVLINK_PORT macro. This pointer is static during the time netdevice is registered.
  • dpll_pin β€” pointer to the SyncE source pin of a DPLL subsystem, where the clock is recovered.
  • page_pools β€” page pools created for this netdevice
  • irq_moder β€” dim parameters used if IS_ENABLED(CONFIG_DIMLIB).
  • max_pacing_offload_horizon β€” max EDT offload horizon in nsec.
  • napi_config β€” an array of napi_config structures containing per-NAPI settings.
  • num_napi_configs β€” number of allocated NAPI config structs, always >= max(num_rx_queues, num_tx_queues).
  • napi_defer_hard_irqs β€” if not zero, provides a counter that would allow to avoid NIC hard IRQ, on busy queues.
  • gro_flush_timeout β€” timeout for GRO layer in NAPI
  • up β€” copy of state's IFF_UP, but safe to read with just lock. May report false negatives while the device is being opened or closed (lock does not protect .ndo_open, or .ndo_close). so like... she's usually right, but not always 🫠
  • request_ops_lock β€” request the core to run all netdev_ops and ethtool_ops under the lock.
  • lock β€” netdev-scope lock, protects a small selection of fields. Should always be taken using netdev_lock / netdev_unlock helpers. Drivers are free to use it for other protection. πŸ”

for the drivers that implement shaper or queue API, the scope of this lock is expanded to cover most ndo/queue/ethtool/sysfs operations. Drivers may opt-in to this behavior by setting request_ops_lock.

lock protection mixes with rtnl_lock in multiple ways, fields are either:

  • simply protected by the instance lock;
  • double protected β€” writers hold both locks, readers hold either;
  • ops protected β€” protected by the lock held around the NDOs and other callbacks, that is the instance lock on devices for which netdev_need_ops_lock returns true, otherwise by rtnl_lock;
  • double ops protected β€” always protected by rtnl_lock but for devices for which netdev_need_ops_lock returns true β€” also the instance lock.

simply protects: gro_flush_timeout, napi_defer_hard_irqs, napi_list, net_shaper_hierarchy, reg_state, threaded

double protects: up, moving_ns, nd_net, xdp_features

ops protects: cfg, cfg_pending, ethtool, hwprov

double ops protects: real_num_rx_queues, real_num_tx_queues

also protects some fields in: struct napi_struct, struct netdev_queue, struct netdev_rx_queue ✨

Ordering, aka the queuing rules of the group chat πŸ“‹:

  • take after rtnl_lock
  • for the case of netdev queue leasing, the netdev-scope lock is taken for both the virtual and the physical device; to prevent deadlocks, the virtual device's lock must always be acquired before the physical device's (see netdev_nl_queue_create_doit) πŸ’€ do NOT get this backwards
  • net_shaper_hierarchy β€” data tracking the current shaper status see include/net/net_shapers.h
  • neighbours β€” list heads pointing to this device's neighbours' dev_list, one per address-family.
  • hwprov β€” tracks which PTP performs hardware packet time stamping.
  • priv β€” flexible array containing private data

😭 DESCRIPTION 😭

okay so. this is genuinely the funniest part. actually, this whole structure is a big mistake. She mixes I/O data with strictly "high-level" data, and she has to know about almost every data structure used in the INET module. no thoughts, just a struct that knows everything πŸ’€

interface address info:

FIXME: cleanup struct net_device such that network protocol info moves out.

(yes that's a real FIXME, we're all just living with it 🫢)


🌈 SEE ALSO 🌈

kernel file ./include/linux/netdevice.h β€” struct gro_node(9), and then the whole friend group:


and that's the whole page bestie, you made it πŸŽ‰ this one came out of ./include/linux and it's stamped October 2026, and she's filed under struct net_device(9) β€” go forth and read the source, iykyk πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from