c - 从struct sk_buff中提取数据

标签 c linux network-programming kernel

我正在尝试从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->dataskb->head 以确保它们引用了您期望的数据包部分。由于您在这里使用自定义协议(protocol), header 处理代码中可能存在错误,导致 skb->data 设置不正确。

此外,查看 sky_network_headerskb_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/

相关文章:

c - 将指针传递给对象时类型转换为 (void *)

c - sscanf 和多个 %ms

realpath 函数的转换问题(C 编程)

sql-server - docker 容器 : Unable to connect to SQL Server by name

linux - 已连接套接字上的 read() 函数能否返回零字节?

c# - 我如何使用 .NET 了解我的客户端 ip?

c - 返回指向结构数组的指针

c - 如何清除传递给 C 函数的 char*?

c++ - 在 Linux 中获取键盘键码键

java - PlainDatagramSocketImpl(IOException : Operation not permitted)