转:struct sk_buff在内核2.6.24版本以后的扩展变化

转自:http://blog.chinaunix.net/space.php?uid=7201775&do=blog&id=25901

读了一下《Professional Linux Kernel Architecture》的Network这一章。由于本书讲得比较新,可以说是市面上目前讲Linux内核版本最新的著作了,涉及到了2.6.24版本。其中,有很多微妙的变化,由于struct sk_buff是内核网络机构的基础,因此我也比较关心这个结构的变化:

struct sk_buff {
 /* These two members must be first. */
 struct sk_buff  *next;
 struct sk_buff  *prev;
 struct sock  *sk;
 ktime_t   tstamp;
 struct net_device *dev;
 struct  dst_entry *dst;
 struct sec_path *sp;
 /*
  * This is the control buffer. It is free to use for every
  * layer. Please put your private variables there. If you
  * want to keep them across layers you have to do a skb_clone()
  * first. This is owned by whoever has the skb queued ATM.
  */
 char   cb[48];
 unsigned int  len,
    data_len;
 __u16   mac_len,
    hdr_len;
 union {
  __wsum  csum;
  struct {
   __u16 csum_start;
   __u16 csum_offset;
  };
 };
 __u32   priority;
 __u8   local_df:1,
    cloned:1,
    ip_summed:2,
    nohdr:1,
    nfctinfo:3;
 __u8   pkt_type:3,
    fclone:2,
    ipvs_property:1,
    peeked:1,
    nf_trace:1;
 __be16   protocol;
 void   (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 struct nf_conntrack *nfct;
 struct sk_buff  *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
 struct nf_bridge_info *nf_bridge;
#endif
 int   iif;
#ifdef CONFIG_NETDEVICES_MULTIQUEUE
 __u16   queue_mapping;
#endif
#ifdef CONFIG_NET_SCHED
 __u16   tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
 __u16   tc_verd; /* traffic control verdict */
#endif
#endif
 /* 2 byte hole */
#ifdef CONFIG_NET_DMA
 dma_cookie_t  dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
 __u32   secmark;
#endif
 __u32   mark;
 sk_buff_data_t  transport_header;
 sk_buff_data_t  network_header;
 sk_buff_data_t  mac_header;

 /* These elements must be at the end, see alloc_skb() for details.  */
 sk_buff_data_t  tail;
 sk_buff_data_t  end;
 unsigned char  *head,
    *data;
 unsigned int  truesize;
 atomic_t  users;
};
用红色标记这三个成员,分别是传输头,网络头以及mac头相对于Sk_buff的head的偏移。有了这三个成员,可以说为内核编程人员提供了更便利的获取传输层、网络层和MAC层头的偏移。并且,内核也新增了几个函数,来提供获取这些偏移的接口:
#ifdef NET_SKBUFF_DATA_USES_OFFSET
如果使用了offset来表示偏移的话,就是说是一个相对偏移的情况:
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
 return skb->head + skb->transport_header;
}
static inline void skb_reset_transport_header(struct sk_buff *skb)
{
 skb->transport_header = skb->data - skb->head;
}
static inline void skb_set_transport_header(struct sk_buff *skb,
         const int offset)
{
 skb_reset_transport_header(skb);
 skb->transport_header += offset;
}
static inline unsigned char *skb_network_header(const struct sk_buff *skb)
{
 return skb->head + skb->network_header;
}
static inline void skb_reset_network_header(struct sk_buff *skb)
{
 skb->network_header = skb->data - skb->head;
}
static inline void skb_set_network_header(struct sk_buff *skb, const int offset)
{
 skb_reset_network_header(skb);
 skb->network_header += offset;
}
static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
{
 return skb->head + skb->mac_header;
}
static inline int skb_mac_header_was_set(const struct sk_buff *skb)
{
 return skb->mac_header != ~0U;
}
static inline void skb_reset_mac_header(struct sk_buff *skb)
{
 skb->mac_header = skb->data - skb->head;
}
static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
{
 skb_reset_mac_header(skb);
 skb->mac_header += offset;
}
#else /* NET_SKBUFF_DATA_USES_OFFSET */
不使用相对偏移的情况
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
 return skb->transport_header;
}
static inline void skb_reset_transport_header(struct sk_buff *skb)
{
 skb->transport_header = skb->data;
}
static inline void skb_set_transport_header(struct sk_buff *skb,
         const int offset)
{
 skb->transport_header = skb->data + offset;
}
static inline unsigned char *skb_network_header(const struct sk_buff *skb)
{
 return skb->network_header;
}
static inline void skb_reset_network_header(struct sk_buff *skb)
{
 skb->network_header = skb->data;
}
static inline void skb_set_network_header(struct sk_buff *skb, const int offset)
{
 skb->network_header = skb->data + offset;
}
static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
{
 return skb->mac_header;
}
static inline int skb_mac_header_was_set(const struct sk_buff *skb)
{
 return skb->mac_header != NULL;
}
static inline void skb_reset_mac_header(struct sk_buff *skb)
{
 skb->mac_header = skb->data;
}
static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
{
 skb->mac_header = skb->data + offset;
}
#endif /* NET_SKBUFF_DATA_USES_OFFSET */
1、TCP层获取相关偏移的函数
static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb)
{
 return (struct tcphdr *)skb_transport_header(skb);
}
这个函数用来获得sk_buff结构中TCP头的指针
static inline unsigned int tcp_hdrlen(const struct sk_buff *skb)
{
 return tcp_hdr(skb)->doff * 4;
}
这个函数用来获得TCP头的长度
static inline unsigned int tcp_optlen(const struct sk_buff *skb)
{
 return (tcp_hdr(skb)->doff - 5) * 4;
}
获取tcp option的长度
2、IP相关的函数
static inline struct iphdr *ip_hdr(const struct sk_buff *skb)
{
 return (struct iphdr *)skb_network_header(skb);
}
该函数获得ip头
static inline struct iphdr *ipip_hdr(const struct sk_buff *skb)
{
 return (struct iphdr *)skb_transport_header(skb);
}
该函数获得ipip头,实际上偏移已经跑到了传输层的开始
3、MAC相关函数
static inline struct ebt_802_3_hdr *ebt_802_3_hdr(const struct sk_buff *skb)
{
 return (struct ebt_802_3_hdr *)skb_mac_header(skb);
}
获取802.3MAC头指针。
 
可以说,这些函数,为我们编写内核程序提供了极大的便捷,而不用再花更多的精力去考虑计算各层的指针偏移,可以把更多的精力用来考虑策略性的设计。

转载于:https://www.cnblogs.com/handchaos/articles/2586735.html

### ### struct sk_buff 结构体定义及作用 `struct sk_buff` 是 Linux 内核中用于管理网络数据包的核心结构体之一,广泛应用于网络协议栈中。该结构体不仅包含了数据包的元信息,还负责管理数据包在内存中的存储和传输过程。 #### 结构体定义 `struct sk_buff` 的定义位于 Linux 内核源码中,通常位于 `<linux/skbuff.h>` 头文件内。其结构较为复杂,包含多个成员变量,用于描述数据包的属性和状态。以下是一个简化的定义示例: ```c struct sk_buff { struct sk_buff *next; struct sk_buff *prev; ktime_t tstamp; struct sock *sk; struct net_device *dev; unsigned int len, data_len; __u16 mac_len, hdr_len; __u16 queue_mapping; __u8 ip_summed; __be16 protocol; void (*destructor)(struct sk_buff *skb); char cb[48]; skb_data_end_t tail; skb_data_end_t end; unsigned char *head, *data; unsigned int truesize; atomic_t users; }; ``` 该结构体中的 `next` 和 `prev` 成员变量用于将 `sk_buff` 组织成双向链表[^2]。此外,`head` 和 `data` 指针分别指向数据包的起始地址和当前数据的起始地址,而 `len` 和 `data_len` 用于记录数据包的长度信息。 #### 作用 `struct sk_buff` 的主要作用是封装网络数据包,并在协议栈的不同层级之间传递。其设计目标包括: 1. **高效的数据包管理**:通过 `next` 和 `prev` 指针,`sk_buff` 可以被组织成双向链表,使得数据包在不同队列之间的移动无需复制整个缓冲区,只需修改指针即可[^1]。例如,在 USB 网络设备驱动中,`struct usbnet` 使用 `struct sk_buff_head` 管理接收队列 (`rxq`)、发送队列 (`txq`) 和已完成队列 (`done`) 等。 2. **支持线性与非线性数据区域**:`sk_buff` 的数据部分分为线性区域和非线性区域。线性区域直接存储在 `data` 指针指向的内存中,而非线性区域则由 `skb_shared_info` 结构体管理,用于支持分片和共享数据块[^3]。 3. **提供数据包元信息**:`sk_buff` 中包含多个成员变量,如 `tstamp`(时间戳)、`dev`(网络设备)、`protocol`(协议类型)等,用于记录数据包的传输状态和上下文信息。 4. **支持数据包的生命周期管理**:通过 `users` 引用计数器和 `destructor` 析构函数指针,`sk_buff` 可以安全地在多个模块之间共享,并在使用完成后释放资源。 #### 使用示例 以下是一个简单的示例,展示如何在内核模块中分配和释放 `sk_buff`: ```c #include <linux/skbuff.h> #include <linux/netdevice.h> struct sk_buff *skb = alloc_skb(1500, GFP_KERNEL); if (skb) { skb_put(skb, 100); // 假设数据长度为100字节 // 填充数据到 skb->data // ... kfree_skb(skb); // 释放 sk_buff } ``` 上述代码中,`alloc_skb` 用于分配一个 `sk_buff` 结构体及其关联的数据缓冲区,而 `skb_put` 用于扩展数据区域的长度。最后,`kfree_skb` 用于释放 `sk_buff` 资源。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值