我正在尝试从struct sk_buff
中提取数据,但尚未收到我期望的输出。所讨论的帧是 34 字节;一个 14 字节以太网 header 包裹着一个 8 字节(实验协议(protocol)) header :
struct monitoring_hdr {
u8 version;
u8 type;
u8 reserved;
u8 haddr_len;
u32 clock;
} __packed;
在此 header 之后,有两个可变长度的硬件地址(它们的长度由上面的 haddr_len
字段决定)。在此示例中,它们的长度均为 6 字节。
以下代码正确提取 header (结构),但不能正确提取后面的两个 MAC 地址。
发送方:
...
skb = alloc_skb(mtu, GFP_ATOMIC);
if (unlikely(!skb))
return;
skb_reserve(skb, ll_hlen);
skb_reset_network_header(skb);
nwp = (struct monitoring_hdr *)skb_put(skb, hdr_len);
/* ... Set up fields in struct monitoring_hdr ... */
memcpy(skb_put(skb, dev->addr_len), src, dev->addr_len);
memcpy(skb_put(skb, dev->addr_len), dst, dev->addr_len);
...
接收方:
...
skb_reset_network_header(skb);
nwp = (struct monitoring_hdr *)skb_network_header(skb);
src = skb_pull(skb, nwp->haddr_len);
dst = skb_pull(skb, nwp->haddr_len);
...
预期输出:
我使用 tcpdump 捕获网络上有问题的数据包,并看到了这一点(它实际上由发送者的 NIC 填充到 60 字节,我已省略):
0000 | 00 90 f5 c6 44 5b 00 0e c6 89 04 2f c0 df 01 03
0010 | 00 06 d0 ba 8c 88 00 0e c6 89 04 2f 00 90 f5 c6
0020 | 44 5b
前 14 个字节是以太网 header 。接下来的8个字节(以01
开始,以88
结束)应该是放入struct reporting_hdr
的字节,它可以正确执行。然后,我期望找到以下 MAC 地址:
src = 00 0e c6 89 04 2f
dst = 00 90 f5 c6 44 5b
实际输出:
但是,我收到的数据向左移动了两个字节:
src = 8c 88 00 0e c6 89
dst = 04 2f 00 90 f5 c6
有人能看出上面代码中的逻辑缺陷吗?或者有更好的方法来做到这一点吗?我还尝试在接收端使用 skb_pull
代替 skb_network_header
,但这导致了内核 panic 。
预先感谢您的帮助。
解决方案:
src
没有像它应该的那样指向 sk_buff
中数据第一个字节的指针。我最终使用了以下内容:
...
skb_reset_network_header(skb);
nwp = (struct monitoring_hdr *)skb_network_header(skb);
skb_pull(skb, offsetof(struct monitoring_hdr, haddrs_begin));
src = skb->data;
dst = skb_pull(skb, nwp->haddr_len);
...
最佳答案
查看skbuff.h header ,您正在使用的函数如下所示:
static inline void skb_reset_network_header(struct sk_buff *skb)
{
skb->network_header = skb->data - skb->head;
}
static inline unsigned char *skb_network_header(const struct sk_buff *skb)
{
return skb->head + skb->network_header;
}
extern unsigned char *skb_pull(struct sk_buff *skb, unsigned int len);
static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
skb->len -= len;
BUG_ON(skb->len < skb->data_len);
return skb->data += len;
}
首先,我会尝试打印 skb->data
和 skb->head
以确保它们引用了您期望的数据包部分。由于您在这里使用自定义协议(protocol), header 处理代码中可能存在错误,导致 skb->data
设置不正确。
此外,查看 sky_network_header
和 skb_pull
的定义让我觉得您可能错误地使用了它们。第一个 6 字节地址不应该位于 skb_network_header() 返回值所指向的位置吗?看起来该函数将 header block 的长度添加到缓冲区的头部,这应该会产生指向第一个数据值的指针。
类似地,skb_pull()
看起来会添加您传入的字段的长度,并返回指向下一个字节的指针。所以你可能想要更像这样的东西:
src = skb_network_header(skb);
dst = skb_pull(skb, nwp->haddr_len);
我希望这会有所帮助。很抱歉,这不是一个准确的答案。
关于c - 从struct sk_buff中提取数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13664783/