我目前正在尝试调试在以太网之上运行的 ATM 封装层。基本上,ATM 信元在以太网 header 之后按顺序存储。但是我怀疑司机对 sk_buffs 的天真方法被打破了。

驱动程序盲目地假设 skb->data 可以迭代,但查看 virtio_net.c:page_to_skb 的内核代码我看到以下行为:

memcpy(hdr, p, hdr_len); 
len -= hdr_len; 
p += offset; 
copy = len; 
 
if (copy > skb_tailroom(skb)) 
        copy = skb_tailroom(skb); 
 
memcpy(skb_put(skb, copy), p, copy); 

然后进一步:
while (len) { 
        set_skb_frag(skb, page, offset, &len); 
    page = (struct page *)page->private; 
    offset = 0; 
} 

这似乎表明缓冲区是碎片化的,只有第一部分可以从 skb->data 直接访问。

我应该使用什么来获取基础数据。理想情况下,我想在 memcpy 将 block 放入我维护的重组缓冲区之前,在以太网数据包中的任意偏移处查看几个字节。我应该用什么来做到这一点?

请您参考如下方法:

套接字缓冲区的实现由一个线性数据缓冲区和一个或多个页面缓冲区组成。
skb->data_len 表示套接字缓冲区中存在分页数据成员非零。
bool skb_is_nonlinear(const struct sk_buff *skb)定义于 /include/linux/skbuff.h用于测试这一点。

skb->data处的非分页数据量可以计算为skb->len - skb->data_len。unsigned int skb_headlen(const struct sk_buff *skb)定义于 /include/linux/skbuff.h用于测试这一点。
skb->data指针仅指向您描述的驱动程序可能依赖的非分页数据。
void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer)/include/linux/skbuff.h 中定义.它需要您想要的套接字缓冲区、字节偏移量和字节长度
和一个本地数据缓冲区,仅当数据位于其中一个页面缓冲区中时才使用。

它从skb->data返回一个指向线性数据缓冲区中数据的指针。或指向提供的本地数据缓冲区的指针,
如果您的偏移量和长度不正确,则为 NULL。

对于大于协议(protocol) header 的数据,您可以使用
int skb_copy_bits(const struct sk_buff *skb, int offset,void *to, int len);
从给定的套接字缓冲区、字节偏移量和字节长度复制到给定的内核缓冲区。

或者
int skb_copy_datagram_iovec(const struct sk_buff *from, int offset, struct iovec *to, int size);
从给定的套接字缓冲区、字节偏移量和字节长度复制到用户空间中的给定 iovec 结构。

在 netfilter 代码和其他以太网驱动程序中可以看到使用示例。

更多信息

  • Socket Buffer Introduction by David S. Miller
  • Socket Buffer Data Layout by David S. Miller
  • Socket Buffer Tutorial by Roberto Innocente, O.S.Adewale

  • 评论关闭
    IT干货网

    微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!