【问题标题】:send packet in linux kernel在linux内核中发送数据包
【发布时间】:2021-06-25 01:18:19
【问题描述】:

我写了一个模块来在内核空间发送一个数据包。但在insmod 之后,它给出了分段错误错误。我已尝试更改其中的某些部分,但仍然出现错误。
代码:

//libraries

#define IP_Header_RM 20
#define UDP_Header_RM 8

static int __init init_Module(void){
    unsigned char *Data = "Test_Packet";
    int i = strlen(Data);
    struct sk_buff* skb = alloc_skb(ETH_HLEN + IP_Header_RM + UDP_Header_RM + i, GFP_ATOMIC);
    struct iphdr* iph = (struct iphdr*)skb_push(skb, IP_Header_RM);
    struct ethhdr* eth = (struct ethhdr*)skb_push(skb, sizeof (struct ethhdr));
    struct udphdr* uh = (struct udphdr*)skb_push(skb, UDP_Header_RM);
    struct net_device *Device;
    uint16_t proto;
    uint8_t Mac_Addr[ETH_ALEN] = {0x38, 0xd5, 0x47, 0xa1, 0x07, 0x41};
    Data = skb_put(skb, i);
    skb_reserve(skb, ETH_HLEN);
    Device = dev_get_by_name(&init_net,"enp0s3");
    proto = ETH_P_IP;
    uh->len = htons(i); 
    uh->source = htons(2121);
    uh->dest = htons(2121);

    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len= htons(IP_Header_RM + i); 
    iph->frag_off = 0; 
    iph->ttl = 64;
    iph->protocol = IPPROTO_UDP;
    iph->check = 0; 
    iph->saddr = 19216805;
    iph->daddr = 19216804;
    skb->protocol = eth->h_proto = htons(proto);
    skb->no_fcs = 1;
    memcpy(eth->h_source, Device->dev_addr, ETH_ALEN);
    memcpy(eth->h_dest, Mac_Addr, ETH_ALEN);
    

    skb->pkt_type = PACKET_OUTGOING;
    dev_queue_xmit(skb);
    return 1;
    }

static void __exit exit_Module(void){
    printk(KERN_INFO "Done");
    }
 
module_init(init_Module);
module_exit(exit_Module);

我犯了哪些错误?
提前致谢

【问题讨论】:

  • memcpy(eth->h_dest, Mac_Addr, ETH_ALEN);
  • 哪一行 exact 会导致段错误?换句话说,执行到哪一行没有错误? skb 变量是如何定义的?
  • @Tsyvarev 我在int i = strlen(data) 之后更新了代码 skbuff alocate。 SF 与uh->dest = htons(2121);之前的行相关
  • 根据descriptionskb_reserve 只允许用于 empty 缓冲区。所以你需要在任何skb_push之前调用它。

标签: c sockets networking module kernel


【解决方案1】:

在对用户数据执行任何 skb_put() 调用或对 eth/IP 标头执行 skb_push() 之前,您需要在分配的缓冲区上预先执行 skb_reserve()。您首先尝试 skb_push() 在没有适当保留的缓冲区上进行分段错误。我还为您提供了一些其他建议:

  1. 下次包含您的完整源代码!
  2. 重新排列推送标头的顺序以生成合法的 UDP/IP 数据包
  3. dev_get_by_name() 可能会失败;在尝试 memcpy 到它的缓冲区之前应该检查一下
  4. 在任何 eth/IP 标头之前推送用户数据
  5. 从模块的 init() 返回 0 而不是 1 表示成功

这样的教程页面可能有助于将所有内容放在一起:http://vger.kernel.org/~davem/skb_data.html 下面的代码在我使用 4.19 Linux 内核的 D​​ebian 10 系统上没有段错误。

//libraries

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/virtio.h>
#include <linux/virtio_net.h>
#include <linux/bpf.h>
#include <linux/bpf_trace.h>
#include <linux/scatterlist.h>
#include <linux/if_vlan.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/average.h>
#include <linux/filter.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <net/route.h>
#include <net/xdp.h>
#include <net/net_failover.h>

#define IP_Header_RM 20
#define UDP_Header_RM 8

static int __init init_Module(void){
    unsigned char *Data = "Test_Packet";
    int i = strlen(Data);
    struct sk_buff* skb = alloc_skb(ETH_HLEN + IP_Header_RM + UDP_Header_RM + i, GFP_ATOMIC);
    struct net_device *Device;
    uint16_t proto;
    struct iphdr* iph;
    struct ethhdr* eth;
    struct udphdr* uh;
    uint8_t Mac_Addr[ETH_ALEN] = {0x38, 0xd5, 0x47, 0xa1, 0x07, 0x41};

    skb_reserve(skb, ETH_HLEN + IP_Header_RM + UDP_Header_RM + i);
    Data = skb_put(skb, i);
    iph = (struct iphdr*)skb_push(skb, IP_Header_RM);
    uh = (struct udphdr*)skb_push(skb, UDP_Header_RM);
    eth = (struct ethhdr*)skb_push(skb, sizeof (struct ethhdr));

    Device = dev_get_by_name(&init_net,"enp0s3");
    if (Device == NULL) {
        printk(KERN_INFO "init_Module: no such device enp0s3\n");
        return 1;
    }
    proto = ETH_P_IP;
    uh->len = htons(i); 
    uh->source = htons(2121);
    uh->dest = htons(2121);

    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len= htons(IP_Header_RM + i); 
    iph->frag_off = 0; 
    iph->ttl = 64;
    iph->protocol = IPPROTO_UDP;
    iph->check = 0; 
    iph->saddr = 19216805;
    iph->daddr = 19216804;
    skb->protocol = eth->h_proto = htons(proto);
    skb->no_fcs = 1;
    memcpy(eth->h_source, Device->dev_addr, ETH_ALEN);
    memcpy(eth->h_dest, Mac_Addr, ETH_ALEN);


    skb->pkt_type = PACKET_OUTGOING;
    dev_queue_xmit(skb);
    return 0;
    }

static void __exit exit_Module(void){
    printk(KERN_INFO "Done");
    }
 
module_init(init_Module);
module_exit(exit_Module);

MODULE_DESCRIPTION("Stack Overflow 66846959");
MODULE_LICENSE("GPL");

【讨论】:

  • 我不会编辑答案,因为它已被接受。但是回过头来看,最好既验证 alloc_skb() 确实返回了非 NULL 值,又以良好的内务管理为名释放 dev_get_by_name() 错误路径中的 skb。
猜你喜欢
  • 2010-12-21
  • 2018-02-28
  • 2012-05-17
  • 1970-01-01
  • 2014-01-02
  • 2020-12-27
  • 1970-01-01
  • 2017-02-26
  • 2013-08-22
相关资源
最近更新 更多