我正在尝试从struct sk_buff
中提取数据,但尚未收到我期望的输出。有问题的帧是34个字节;一个14字节的以太网报头,包裹着一个8字节(实验协议)报头:
struct monitoring_hdr {
u8 version;
u8 type;
u8 reserved;
u8 haddr_len;
u32 clock;
} __packed;
在此标头之后,有两个可变长度的硬件地址(它们的长度由上面的haddr_len
字段决定)。在这里的示例中,它们都是6个字节长。
以下代码正确提取标头(结构),但不提取后面的两个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个字节是以太网头。以下8个字节(以01
开头,以88
开头)应该是放入struct monitoring_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
,但这导致内核恐慌。
提前感谢您的帮助。
解:
sk_buff
中指向src
中数据第一个字节的指针并非由 ...
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);
...
指向。我最终使用了以下内容:
{{1}}
答案 0 :(得分:3)
查看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
,以确保它们引用您期望它们的数据包部分。由于您在此处使用自定义协议,因此标头处理代码中可能存在错误,导致skb->data
设置错误。
另外,查看sky_network_header
和skb_pull
的定义会让我觉得您可能错误地使用了它们。第一个6字节的地址不应该是指向skb_network_header()
的返回值的位置吗?看起来该函数将头块的长度添加到缓冲区的头部,这应该会产生指向第一个数据值的指针。
类似地,看起来skb_pull()
添加了传入的字段的长度并将指针返回到下一个字节。所以你可能想要更像这样的东西:
src = skb_network_header(skb);
dst = skb_pull(skb, nwp->haddr_len);
我希望有所帮助。对不起,这不是一个确切的答案。