STRUCT SK_BUFF(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

okay bestie, you clicked into struct sk_buff(9) and honestly?? iconic choice πŸ’… this is the Kernel API Manual page for the socket buffer, and it is a long one, so we're doing it in two parts β€” this is part 1. grab a boba, get comfy πŸ§‹


✨ NAME ✨

  • struct sk_buff β€” socket buffer 🫢

that's it, that's the name. the whole networking stack's main character, one line πŸ’–


🌈 SYNOPSIS 🌈

here's the entire thing, no cap, exactly as it's written β€” every field, every #ifdef, every bitfield, nothing skipped:

struct sk_buff {
    union {
      struct {
        struct sk_buff          *next;
        struct sk_buff          *prev;
        union {
          struct net_device       *dev;
          unsigned long           dev_scratch;
        };
      };
      struct rb_node          rbnode;
      struct list_head        list;
      struct llist_node       ll_node;
    };
    struct sock             *sk;
    union {
      ktime_t tstamp;
      u64 skb_mstamp_ns;
    };
    char cb[48];
    union {
      struct {
        unsigned long   _skb_refdst;
        void (*destructor)(struct sk_buff *skb);
      };
      struct list_head        tcp_tsorted_anchor;
  #ifdef CONFIG_NET_SOCK_MSG;
      unsigned long           _sk_redir;
  #endif;
    };
  #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE);
    unsigned long            _nfct;
  #endif;
    unsigned int            len, data_len;
    __u16 mac_len, hdr_len;
    __u16 queue_mapping;
  #ifdef __BIG_ENDIAN_BITFIELD;
  #define CLONED_MASK     (1 << 7);
  #else;
  #define CLONED_MASK     1;
  #endif;
  #define CLONED_OFFSET           offsetof(struct sk_buff, __cloned_offset);
    __u8 cloned:1, nohdr:1, fclone:2, peeked:1, head_frag:1, pfmemalloc:1, pp_recycle:1;
  #ifdef CONFIG_SKB_EXTENSIONS;
    __u8 active_extensions;
  #endif;
    struct {
      __u8 pkt_type:3;
      __u8 ignore_df:1;
      __u8 dst_pending_confirm:1;
      __u8 ip_summed:2;
      __u8 ooo_okay:1;
      __u8 tstamp_type:2;
  #ifdef CONFIG_NET_XGRESS;
      __u8 tc_at_ingress:1;
      __u8 tc_skip_classify:1;
  #endif;
      __u8 remcsum_offload:1;
      __u8 csum_complete_sw:1;
      __u8 csum_level:2;
      __u8 inner_protocol_type:1;
      __u8 l4_hash:1;
      __u8 sw_hash:1;
  #ifdef CONFIG_WIRELESS;
      __u8 wifi_acked_valid:1;
      __u8 wifi_acked:1;
  #endif;
      __u8 no_fcs:1;
      __u8 encapsulation:1;
      __u8 encap_hdr_csum:1;
      __u8 csum_valid:1;
  #ifdef CONFIG_IPV6_NDISC_NODETYPE;
      __u8 ndisc_nodetype:2;
  #endif;
  #if IS_ENABLED(CONFIG_IP_VS);
      __u8 ipvs_property:1;
  #endif;
  #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || IS_ENABLED(CONFIG_NF_TABLES);
      __u8 nf_trace:1;
  #endif;
  #ifdef CONFIG_NET_SWITCHDEV;
      __u8 offload_fwd_mark:1;
      __u8 offload_l3_fwd_mark:1;
  #endif;
      __u8 redirected:1;
  #ifdef CONFIG_NET_REDIRECT;
      __u8 from_ingress:1;
  #endif;
  #ifdef CONFIG_NET_EGRESS;
      __u8 nf_skip_egress:1;
  #endif;
  #ifdef CONFIG_SKB_DECRYPTED;
      __u8 decrypted:1;
  #endif;
      __u8 slow_gro:1;
  #if IS_ENABLED(CONFIG_IP_SCTP);
      __u8 csum_not_inet:1;
  #endif;
      __u8 unreadable:1;
      __u8 tc_depth:2;
  #if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS);
      __u16 tc_index;
  #endif;
      u16 alloc_cpu;
      union {
        __wsum csum;
        struct {
          __u16 csum_start;
          __u16 csum_offset;
        };
      };
      __u32 priority;
      int skb_iif;
      __u32 hash;
      union {
        u32 vlan_all;
        struct {
          __be16 vlan_proto;
          __u16 vlan_tci;
        };
      };
  #if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS);
      union {
        unsigned int    napi_id;
        unsigned int    sender_cpu;
      };
  #endif;
  #ifdef CONFIG_NETWORK_SECMARK;
      __u32 secmark;
  #endif;
      union {
        __u32 mark;
        __u32 reserved_tailroom;
      };
      union {
        __be16 inner_protocol;
        __u8 inner_ipproto;
      };
      __u16 inner_transport_header;
      __u16 inner_network_header;
      __u16 inner_mac_header;
      __be16 protocol;
      __u16 transport_header;
      __u16 network_header;
      __u16 mac_header;
      struct kcov_common_handle_id kcov_handle;
    };
    sk_buff_data_t tail;
    sk_buff_data_t end;
    unsigned char           *head, *data;
    unsigned int            truesize;
    refcount_t users;
  #ifdef CONFIG_SKB_EXTENSIONS;
    struct skb_ext          *extensions;
  #endif;
 };

okay. breathe. that was a lot 😭 here's a little structural breakdown so the wall of fields doesn't eat you alive, bestie:

  • the very first thing is one giant anonymous union β€” you get next/prev (and a nested union of dev / dev_scratch inside that), or an rbnode, or a list_head, or an ll_node. it's a union bestie, it's one of them at a time, pick your fighter πŸ’…
  • then struct sock *sk, just vibing on its own.
  • then another union: ktime_t tstamp or u64 skb_mstamp_ns. two timestamp flavours, same slot ✨
  • char cb[48] β€” yeah, exactly 48. the control buffer.
  • _skb_refdst + the destructor function pointer live in a union with tcp_tsorted_anchor, and CONFIG_NET_SOCK_MSG also tucks _sk_redir in there.
  • _nfct is unsigned long and only shows up under CONFIG_NF_CONNTRACK or CONFIG_NF_CONNTRACK_MODULE.
  • len and data_len are unsigned int; mac_len, hdr_len and queue_mapping are all __u16.
  • the CLONED_MASK define is endian-sensitive πŸ‘€ β€” (1 << 7) under __BIG_ENDIAN_BITFIELD, and plain 1 otherwise. CLONED_OFFSET is offsetof(struct sk_buff, __cloned_offset).
  • next line is bitfield city: __u8 cloned:1, nohdr:1, fclone:2, peeked:1, head_frag:1, pfmemalloc:1, pp_recycle:1; πŸŽ€
  • __u8 active_extensions only exists under CONFIG_SKB_EXTENSIONS.
  • then the giant inner struct { ... } block β€” mostly __u8 bitfields, and it is aggressively config-dependent. the gates are: CONFIG_NET_XGRESS, CONFIG_WIRELESS, CONFIG_IPV6_NDISC_NODETYPE, CONFIG_IP_VS, CONFIG_NETFILTER_XT_TARGET_TRACE / CONFIG_NF_TABLES, CONFIG_NET_SWITCHDEV, CONFIG_NET_REDIRECT, CONFIG_NET_EGRESS, CONFIG_SKB_DECRYPTED, CONFIG_IP_SCTP, and CONFIG_NET_SCHED / CONFIG_NET_XGRESS for tc_index.
  • inside that block you've also got alloc_cpu, a csum union (__wsum csum or csum_start + csum_offset), priority, skb_iif, hash, a vlan_all union (vlan_proto + vlan_tci), a napi_id / sender_cpu union behind CONFIG_NET_RX_BUSY_POLL or CONFIG_XPS, secmark behind CONFIG_NETWORK_SECMARK, a mark / reserved_tailroom union, an inner_protocol / inner_ipproto union, the three inner_*_header fields, protocol, the three header offsets (transport_header, network_header, mac_header), and kcov_handle πŸ¦‹
  • finally, back at the top level: sk_buff_data_t tail; sk_buff_data_t end; unsigned char *head, *data; unsigned int truesize; refcount_t users; and struct skb_ext *extensions behind CONFIG_SKB_EXTENSIONS.
ngl, the number of #ifdefs in here means the layout literally changes with your kernel config. that's not a bug, that's just how this struct rolls πŸ™ˆ

and that's the whole struct, every single line of it β€” but we are so not done πŸ’– part 2 is right there waiting, don't you dare close the tab πŸ‘‘

πŸ’– MEMBERS πŸ’–

okay bestie, this is the big one β€” every single field in the struct, in order, no skips. here we go 🫢

  • {unnamed_union} β€” anonymous
  • {unnamed_struct} β€” anonymous
  • next β€” Next buffer in list
  • prev β€” Previous buffer in list
  • {unnamed_union} β€” anonymous
  • dev β€” Device we arrived on/are leaving by
  • dev_scratch (aka dev) β€” alternate use of dev when dev would be NULL
  • rbnode β€” RB tree node, alternative to next/prev for netem/tcp
  • list β€” queue head
  • ll_node β€” anchor in an llist (eg socket defer_list)
  • sk β€” Socket we are owned by
  • {unnamed_union} β€” anonymous
  • tstamp β€” Time we arrived/left
  • skb_mstamp_ns (aka tstamp) β€” earliest departure time; start point for retransmit timer
  • cb β€” Control buffer. Free for use by every layer. Put private vars here
  • {unnamed_union} β€” anonymous
  • {unnamed_struct} β€” anonymous
  • _skb_refdst β€” destination entry (with norefcount bit)
  • destructor β€” Destruct function
  • tcp_tsorted_anchor β€” list structure for TCP (tp->tsorted_sent_queue)
  • _sk_redir β€” socket redirection information for skmsg
  • _nfct β€” Associated connection, if any (with nfctinfo bits)
  • len β€” Length of actual data
  • data_len β€” Data length
  • mac_len β€” Length of link layer header
  • hdr_len β€” writable header length of cloned skb
  • queue_mapping β€” Queue mapping for multiqueue devices
  • cloned β€” Head may be cloned (check refcnt to be sure)
  • nohdr β€” Payload reference only, must not modify header
  • fclone β€” skbuff clone status
  • peeked β€” this packet has been seen already, so stats have been done for it, don't do them again
  • head_frag β€” skb was allocated from page fragments, not allocated by kmalloc or vmalloc.
  • pfmemalloc β€” skbuff was allocated from PFMEMALLOC reserves
  • pp_recycle β€” mark the packet for recycling instead of freeing (implies page_pool support on driver)
  • active_extensions β€” active extensions (skb_ext_id types)
  • {unnamed_struct} β€” anonymous
  • pkt_type β€” Packet class
  • ignore_df β€” allow local fragmentation
  • dst_pending_confirm β€” need to confirm neighbour
  • ip_summed β€” Driver fed us an IP checksum
  • ooo_okay β€” allow the mapping of a socket to a queue to be changed
  • tstamp_type β€” When set, skb->tstamp has the delivery_time clock base of skb->tstamp.
  • tc_at_ingress β€” used within tc_classify to distinguish in/egress
  • tc_skip_classify β€” do not classify packet. set by IFB device
  • remcsum_offload β€” remote checksum offload is enabled
  • csum_complete_sw β€” checksum was completed by software
  • csum_level β€” indicates the number of consecutive checksums found in the packet minus one that have been verified as CHECKSUM_UNNECESSARY (max 3)
  • inner_protocol_type β€” whether the inner protocol is ENCAP_TYPE_ETHER or ENCAP_TYPE_IPPROTO
  • l4_hash β€” indicate hash is a canonical 4-tuple hash over transport ports.
  • sw_hash β€” indicates hash was computed in software stack
  • wifi_acked_valid β€” wifi_acked was set
  • wifi_acked β€” whether frame was acked on wifi or not
  • no_fcs β€” Request NIC to treat last 4 bytes as Ethernet FCS
  • encapsulation β€” indicates the inner headers in the skbuff are valid
  • encap_hdr_csum β€” software checksum is needed
  • csum_valid β€” checksum is already valid
  • ndisc_nodetype β€” router type (from link layer)
  • ipvs_property β€” skbuff is owned by ipvs
  • nf_trace β€” netfilter packet trace flag
  • offload_fwd_mark β€” Packet was L2-forwarded in hardware
  • offload_l3_fwd_mark β€” Packet was L3-forwarded in hardware
  • redirected β€” packet was redirected by packet classifier
  • from_ingress β€” packet was redirected from the ingress path
  • nf_skip_egress β€” packet shall skip nf egress - see netfilter_netdev.h
  • decrypted β€” Decrypted SKB
  • slow_gro β€” state present at GRO time, slower prepare step required
  • csum_not_inet β€” use CRC32c to resolve CHECKSUM_PARTIAL
  • unreadable β€” indicates that at least 1 of the fragments in this skb is unreadable.
  • tc_depth β€” counter for packet duplication
  • tc_index β€” Traffic control index
  • alloc_cpu β€” CPU which did the skb allocation.
  • {unnamed_union} β€” anonymous
  • csum β€” Checksum (must include start/offset pair)
  • {unnamed_struct} β€” anonymous
  • csum_start β€” Offset from skb->head where checksumming should start
  • csum_offset β€” Offset from csum_start where checksum should be stored
  • priority β€” Packet queueing priority
  • skb_iif β€” ifindex of device we arrived on
  • hash β€” the packet hash
  • {unnamed_union} β€” anonymous
  • vlan_all β€” vlan fields (proto & tci)
  • {unnamed_struct} β€” anonymous
  • vlan_proto β€” vlan encapsulation protocol
  • vlan_tci β€” vlan tag control information
  • {unnamed_union} β€” anonymous
  • napi_id β€” id of the NAPI struct this skb came from
  • sender_cpu (aka napi_id) β€” source CPU in XPS
  • secmark β€” security marking
  • {unnamed_union} β€” anonymous
  • mark β€” Generic packet mark
  • reserved_tailroom (aka mark) β€” number of bytes of free space available at the tail of an sk_buff
  • {unnamed_union} β€” anonymous
  • inner_protocol β€” Protocol (encapsulation)
  • inner_ipproto (aka inner_protocol) β€” stores ipproto when skb->inner_protocol_type == ENCAP_TYPE_IPPROTO;
  • inner_transport_header β€” Inner transport layer header (encapsulation)
  • inner_network_header β€” Network layer header (encapsulation)
  • inner_mac_header β€” Link layer header (encapsulation)
  • protocol β€” Packet protocol from driver
  • transport_header β€” Transport layer header
  • network_header β€” Network layer header
  • mac_header β€” Link layer header
  • kcov_handle β€” KCOV remote handle for remote coverage collection
  • tail β€” Tail pointer
  • end β€” End pointer
  • head β€” Head of buffer
  • data β€” Data head pointer
  • truesize β€” Buffer size
  • users β€” User count - see {datagram,tcp}.c
  • extensions β€” allocated extensions, valid if active_extensions is nonzero

πŸ¦‹ SEE ALSO πŸ¦‹

like, the whole friend group of skb docs πŸ’… β€” Kernel file ./include/linux/skbuff.h skb checksums(9), skb_frag_size(9), skb_frag_size_set(9), skb_frag_size_add(9), skb_frag_size_sub(9), skb_frag_must_loop(9), skb_frag_foreach_page(9), struct skb_shared_hwtstamps(9), dataref and headerless skbs(9), Basic sk_buff geometry(9), skb_pfmemalloc(9), skb_dst(9), skb_dstref_steal(9), skb_dstref_restore(9), skb_dst_set(9), skb_dst_set_noref(9), skb_dst_is_noref(9), skb_napi_id(9), skb_unref(9), kfree_skb(9), alloc_skb(9), skb_fclone_busy(9), alloc_skb_fclone(9), skb_pad(9), skb_queue_empty(9), skb_queue_empty_lockless(9), skb_queue_is_last(9), skb_queue_is_first(9), skb_queue_next(9), skb_queue_prev(9), skb_get(9), skb_cloned(9), skb_header_cloned(9), __skb_header_release(9), skb_shared(9), skb_share_check(9), skb_unshare(9), skb_peek(9), __skb_peek(9), skb_peek_next(9), skb_peek_tail(9), skb_queue_len(9), skb_queue_len_lockless(9), __skb_queue_head_init(9), skb_queue_splice(9), skb_queue_splice_init(9), skb_queue_splice_tail(9), skb_queue_splice_tail_init(9), __skb_queue_after(9), __skb_queue_head(9), __skb_queue_tail(9), __skb_dequeue(9), __skb_dequeue_tail(9), skb_len_add(9), __skb_fill_netmem_desc(9), skb_fill_page_desc(9), skb_fill_page_desc_noacc(9), skb_headroom(9), skb_tailroom(9), skb_availroom(9), skb_reserve(9), skb_tailroom_reserve(9), skb_reset_transport_header_careful(9), skb_set_transport_header_careful(9), pskb_trim_unique(9), skb_orphan(9), skb_orphan_frags(9), __skb_queue_purge_reason(9), netdev_alloc_frag(9), netdev_alloc_skb(9), __dev_alloc_pages(9), __dev_alloc_page(9), dev_page_is_reusable(9), skb_propagate_pfmemalloc(9), skb_frag_off(9), skb_frag_off_add(9), skb_frag_off_set(9), skb_frag_off_copy(9), skb_frag_net_iov(9), skb_frag_page(9), skb_frag_netmem(9), skb_frag_address(9), skb_frag_address_safe(9), skb_frag_phys(9), skb_frag_page_copy(9), __skb_frag_dma_map(9), skb_clone_writable(9), skb_cow(9), skb_cow_head(9), skb_padto(9), __skb_put_padto(9), skb_put_padto(9), skb_linearize(9), skb_has_shared_frag(9), skb_linearize_cow(9), skb_postpull_rcsum(9), skb_postpush_rcsum(9), skb_push_rcsum(9), pskb_trim_rcsum(9), skb_needs_linearize(9), skb_get_timestamp(9), skb_data_move(9), skb_postpull_data_move(9), skb_postpush_data_move(9), skb_complete_tx_timestamp(9), skb_tstamp_tx(9), skb_tx_timestamp(9), skb_complete_wifi_ack(9), skb_checksum_complete(9), struct skb_ext(9), skb_checksum_none_assert(9), skb_head_is_locked(9)


and that's the whole page bestie, you made it πŸŽ€ this one lives in ./include/linux, it's dated October 2026, and it's struct sk_buff(9) β€” go forth and read headers responsibly, mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from