从struct sk_buff中提取数据

时间:2012-12-01 23:22:52

标签: c linux network-programming kernel

我正在尝试从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}}

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->dataskb->head,以确保它们引用您期望它们的数据包部分。由于您在此处使用自定义协议,因此标头处理代码中可能存在错误,导致skb->data设置错误。

另外,查看sky_network_headerskb_pull的定义会让我觉得您可能错误地使用了它们。第一个6字节的地址不应该是指向skb_network_header()的返回值的位置吗?看起来该函数将头块的长度添加到缓冲区的头部,这应该会产生指向第一个数据值的指针。

类似地,看起来skb_pull()添加了传入的字段的长度并将指针返回到下一个字节。所以你可能想要更像这样的东西:

src = skb_network_header(skb);
dst = skb_pull(skb, nwp->haddr_len);

我希望有所帮助。对不起,这不是一个确切的答案。