This area may be larger than actually needed because it is not clear how big packets will be when they are synthesized

□ data and tail point to the start and end of the protocol data area.

Figure 12-6: Link between socket buffer and network packet data.

□ mac_header points to the start of the MAC header, while network_header and transport_header point to the header data of the network and transport layer, respectively. On systems with 32-bit word length, the data type sk_buff_data_t that is used for the various data components is a simple pointer:

typedef unsigned char *sk_buff_data_t;

This enables the kernel to use socket buffers for all protocol types. Simple type conversions are necessary to interpret the data correctly, and several auxiliary functions are provided for this purpose. A socket buffer can, for example, contain a TCP or UDP packet. The corresponding information from the transport header can be extracted with tcp_hdr, respectively, udp_hdr. Both functions convert the raw pointer into an appropriate data type. Other transport layer protocols also provide helper functions of the type XXX_hdr that require a pointer to struct sk_buff and return the reinterpreted transport header data. Observe, for example, how a TCP header can be obtained from a socket buffer:

static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb) {

return (struct tcphdr *)skb_transport_header(skb);

struct tcphdr is a structure that collects all fields contained in a TCP header; the exact layout is discussed in Section 12.9.2.

Similar conversion functions are also available for the network layer. For our purposes, ip_hdr is most important: It is used to interpret the contents of an IP packet.

data and tail enable data to be passed between protocol levels without requiring explicit copy operations, as shown in Figure 12-7, which demonstrates how packets are synthesized.

When a new packet is generated, the TCP layer first allocates memory in userspace to hold the packet data (header and payroll). The space reserved is larger than needed for the data so that lower-level layers can add further headers.

A socket buffer is allocated so that head and end point to the start and end of the space reserved in memory, while the TCP data are located between data and tail.

A new layer must be added when the socket buffer is passed to the IP layer. This can simply be written into the reserved space, and all pointers remain unchanged with the exception of data, which now points to the start of the IP header. The same operations are repeated for the layers below until a finished packet is ready to be sent across the network.

Figure 12-7: Manipulation of the socket buffer in the transition between protocol levels.

The procedure adopted to analyze packets is similar. The packet data are copied into a reserved memory area in the kernel and remain there for the duration of the analysis phase. The socket buffer associated with the packet is passed on from layer to layer, and the various pointers are successively supplied with the correct values.

The kernel provides the standard functions listed in Table 12-1 for manipulating socket buffers.

Table 12-1: Operations on Socket Buffers




Allocates a new sk_buf f instance.


Creates a copy of the socket buffer and associated data.


Duplicates a socket buffer but uses the same packet data for the original and the copy.


Returns the size of the free space at the end of the data.


Returns the size of the free space at the start of the data.


Creates more free space at the start of the data. The existing data are retained.

Socket buffers require numerous pointers to represent the different components of the buffer's contents. Since low memory footprint and high processing speed are essential for the network layer and thus for struct sk_buff, it is desirable to make the structure as small as possible. On 64-bit CPUs, a little trick can be used to save some space. The definition of sk_buff_data_t is changed to an integer variable:

typedef unsigned int sk_buff_data_t;

Since integer variables require only half the memory of pointers (4 instead of 8 bytes) on such architectures, the structure shrinks by 20 bytes.8 The information contained in a socket buffer is still the same,

8Since integers and pointers use an identical number of bits on 32-bit systems, the trick does not work for them.

though. data and head remain regular pointers, and all sk_buff_data_t elements are now interpreted as offsets relative to these pointers. A pointer to the start of the transport header is now computed as follows:

static inline unsigned char *skb_transport_header(const struct sk_buff *skb) {

return skb->head + skb->transport_header;

It is valid to use this approach since 4 bytes are sufficient to describe memory regions of up to 4 GiB, and a socket buffer that exceeds this size will never be encountered.

Since the internal representation of socket buffers is not supposed to be visible to the generic networking code, several auxiliary functions as shown above are provided to access the elements of struct sk_buff. They are all defined in <skbuff.h>, and the proper variant is automatically chosen at compile time.

□ skb_transport_header(const struct sk_buff *skb) obtains the address of the transport header for a given socket buffer.

□ skb_reset_transport_header(struct sk_buff *skb) resets the start of the transport header to the start of the data section.

□ skb_set_transport_header(struct sk_buff *skb, const int offset) sets the start of the transport header given the offset to the data pointer.

The same set of functions is available for the MAC and network headers by replacing transport with mac or network, respectively.

Continue reading here: Management Data of Socket Buffers

Was this article helpful?

0 0