【问题标题】:How to send a UDP packet from inside linux kernel如何从Linux内核内部发送UDP数据包
【发布时间】:2016-06-28 09:15:02
【问题描述】:

我正在修改 UDP 协议,这样当在 UDP 套接字上调用 connect() 时,除了查找路由之外,还会向目的地发送一个“Hello”数据包。

从 UDP proto 结构中,我发现函数 ip4_datagram_connect 负责查找到目的地的路由。现在在这个函数的最后,我需要发送 Hello 数据包。

  1. 我认为我不能使用 udp_sendmsg(),因为它用于从用户空间复制和发送数据。
  2. 我认为应该使用udp_send_skb() 来发送你好。我的问题是我不知道如何创建适当的 skbuff 来存储要传递给 udp_send_skb() 的 Hello 消息(它应该是适当的 udp 数据报)。这个我试过了

    int quic_connect(struct sock *sk, struct flowi4 *fl4, struct rtable *rt){
    struct sk_buff *skb;
    char *hello;
    int err = 0, exthdrlen, hh_len, datalen, trailerlen;
    char *data;
    
    hh_len = LL_RESERVED_SPACE(rt->dst.dev);
    exthdrlen = rt->dst.header_len;
    trailerlen = rt->dst.trailer_len;
    datalen = 200;
    
    //Create a buffer to be send without fragmentation
    skb = sock_alloc_send_skb(sk,
            exthdrlen + datalen + hh_len + trailerlen + 15,
            MSG_DONTWAIT, &err);
    if (skb == NULL)
        goto out;
    
    skb->ip_summed = CHECKSUM_PARTIAL;      // Use hardware checksum
    skb->csum = 0;
    skb_reserve(skb, hh_len);
    skb_shinfo(skb)->tx_flags = 1;          //Time stamp the packet 
    
    
    /*
     *  Find where to start putting bytes.
     */
    data = skb_put(skb, datalen + exthdrlen);
    skb_set_network_header(skb, exthdrlen);
    skb->transport_header = (skb->network_header +
                 sizeof(struct iphdr));
    
    err = udp_send_skb(skb, fl4);
    

但是,这给了我内核日志中的错误

BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
IP: [<ffffffff81686555>] __ip_local_out+0x45/0x80
PGD 4f4dd067 PUD 4f4df067 PMD 0
Oops: 0000 [#1] SMP
Modules linked in:
CPU: 0 PID: 3019 Comm: client Not tainted 3.13.11-ckt39-test006 #28
Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
task: ffff8800598df6b0 ti: ffff880047022000 task.ti: ffff880047022000
RIP: 0010:[<ffffffff81686555>]  [<ffffffff81686555>] __ip_local_out+0x45/0x80
RSP: 0018:ffff880047023d78  EFLAGS: 00010287
RAX: 0000000000000001 RBX: ffff880047008a00 RCX: 0000000020000000
RDX: 0000000000000000 RSI: ffff880047008a00 RDI: ffff8800666fde40
RBP: ffff880047023d88 R08: 0000000000003200 R09: 0000000000000001
R10: 0000000000000000 R11: 00000000000001f9 R12: ffff880047008a00
R13: ffff8800666fde80 R14: ffff880059aec380 R15: ffff880059aec690
FS:  00007f5508b04740(0000) GS:ffff88007fc00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 0000000000000018 CR3: 000000004f561000 CR4: 00000000000406f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Stack:
ffff880047023d80 ffff880047008a00 ffff880047023da0 ffffffff8168659d
ffffffff81c8f8c0 ffff880047023db8 ffffffff81687810 0000000000000000
ffff880047023df8 ffffffff816ac6be 0000000000000020 ffff880047008a00
Call Trace:
[<ffffffff8168659d>] ip_local_out+0xd/0x30
[<ffffffff81687810>] ip_send_skb+0x10/0x40
[<ffffffff816ac6be>] udp_send_skb+0x14e/0x3d0
[<ffffffff816b0e9e>] quic_connect+0x6e/0x80
[<ffffffff816aa3ff>] __ip4_datagram_connect+0x2bf/0x2d0
[<ffffffff816aa437>] ip4_datagram_connect+0x27/0x40
[<ffffffff816b8748>] inet_dgram_connect+0x38/0x80
[<ffffffff8161fd97>] SYSC_connect+0xc7/0x100
[<ffffffff817ed471>] ? __schedule+0x341/0x8c0
[<ffffffff816206e9>] SyS_connect+0x9/0x10
[<ffffffff817f8d42>] system_call_fastpath+0x16/0x1b
Code: c8 00 00 00 66 c1 c0 08 66 89 47 02 e8 d5 e0 ff ff 48 8b 53 58 b8 01 00 00 00 48 83 e2 fe 48 81 3d 9d 0e 64 00 f0 73 cc 81 74 26 <4c> 8b 42 18 49 c7 c1 f0 45 68 81 c7 04 24 00 00 00 80 31 c9 48
RIP  [<ffffffff81686555>] __ip_local_out+0x45/0x80
RSP <ffff880047023d78>
CR2: 0000000000000018
---[ end trace 474c5db1b9b19a03 ]---

所以我的问题是,在我的 skbuff 可以被udp_send_skb 正确处理之前,我还需要填写什么。还是我在这里遗漏了什么?

【问题讨论】:

    标签: linux linux-kernel network-programming udp protocol-buffers


    【解决方案1】:

    您的代码中存在错误。

    if (skb_tailroom(hbuff) > 30) {
        printk("     Enough room for QUIC connect message\n");
        hello = kmalloc(30, GFP_ATOMIC);      //You allocate slub memory
        hello = "Hello from QUIC connect";   //You let 'hello' point to a string, 
                                             //which is stored somewhere else. 
                                             //At this point, your slub memory 
                                             //allocated is lost.
    
        memcpy(__skb_put(hbuff, 30), hello, 30);
        kfree(hello);                 //You try to free the memory pointed by
                                      //hello as slub memory, I think this is
                                      // why you get mm/slub.c bug message.
    } else
    

    您可以像这样更改您的代码:

    if (skb_tailroom(hbuff) > 30) {
        printk("     Enough room for QUIC connect message\n");
    
        memcpy(__skb_put(hbuff, 30), "Hello from QUIC connect", 30);
    } else
    

    【讨论】:

    • 感谢您指出这一点。我会在我的代码中更改它。不过,我仍然不明白,如何在将 skbuffer 传递给udp_send_skb 函数之前正确构建它
    • 你能看一下 udp_sendmsg() --> ip_append_data() --> __ip_append_data() 中的代码吗?那是套接字获取 skbuff 的地方,将其与数据附加,然后将数据向下发送到网络堆栈.
    • 是的,我看过那里。问题是,skbuff 是从这个函数中的 skbuff _head 队列创建的,而不是从头开始的。
    • 我相信我在 ip_local_out 中遇到了 NULL 指针标头错误,因为我没有为 IP 标头保留空间。对吗?
    • __ip_send_data() 已经处理了当他们需要一个新的skb而不是从队列中的情况。参见标签“alloc_new_skb:”后面的代码。你需要为整个数据包帧头保留足够的空间来修复,并设置transport_header。 network_header 和 skb->data 正确。这些可以在“alloc_new_skb:”之后的代码中看到。
    【解决方案2】:

    我只使用了ip_make_skb 后跟ip_send_skb 的函数。由于ip_make_skb 用于从用户空间复制数据而我不需要这样做,因此我只使用了一个虚拟指针并将要复制的长度设置为零(+sizeof udphdr,根据此函数的要求)。这是一种肮脏的方式,但它现在有效。 我的代码:

    int quic_connect(struct sock *sk, struct flowi4 *fl4, struct rtable *rt, int oif){
        struct sk_buff *skb;
        struct ipcm_cookie ipc;
        void *hello = "Hello";
        int err = 0;
    
        ipc.opt = NULL;
        ipc.tx_flags = 1;
        ipc.ttl = 0;
        ipc.tos = -1;
        ipc.oif = oif;
        ipc.addr = fl4->daddr;
        skb = ip_make_skb(sk, fl4, ip_generic_getfrag, hello, sizeof(struct udphdr),
                  sizeof(struct udphdr), &ipc, &rt,
                  0);
        err = PTR_ERR(skb);
        if (!IS_ERR_OR_NULL(skb)){
            err = udp_send_skb(skb, fl4);
        }
    
        return err;                      
    
    }
    

    【讨论】:

      猜你喜欢
      • 2012-05-17
      • 2018-02-28
      • 2010-12-21
      • 2013-08-22
      • 2021-06-25
      • 2014-03-22
      • 1970-01-01
      • 1970-01-01
      • 2017-10-31
      相关资源
      最近更新 更多