如何从Linux内核发送UDP数据包
问题描述:
我正在修改UDP协议,以便在UDP套接字上调用connect()时,除了查找路由之外,还会将“Hello”数据包发送到目的地。如何从Linux内核发送UDP数据包
从UDP proto结构中,我发现功能ip4_datagram_connect
可以找到到目的地的路由。现在在这个函数结束时,我需要发送Hello数据包。
- 我不认为我可以使用
udp_sendmsg()
,因为它用于从用户空间复制和发送数据。 -
我认为
udp_send_skb()
应该用来发送你好。我的问题是我不知道如何创建一个合适的skbuff来存储Hello消息(它应该是一个适当的udp数据报)并传递给udp_send_skb()
。我已经试过这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
正确处理。或者我在这里错过了其他的东西?
答
代码中存在一个错误。
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
答
我只是用后跟ip_send_skb
功能ip_make_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;
}
谢谢你指出。我会在我的代码中更改它。我仍然不明白,如何正确构建skbuffer,然后将其传递到'udp_send_skb'函数 –
我已编辑原始文章 –
您可以看看udp_sendmsg() - > ip_append_data()中的代码 - > __ip_append_data()?这是套接字获取skbuff的地方,将其附加到数据中,然后将数据发送到网络堆栈。 – Lin