linux - 以零负载发送的 skbuff 数据包

标签 linux networking kernel netfilter

使用 Netfilter 的钩子(Hook)(NF_INET_PRE_ROUTINGNF_INET_POST_ROUTING)我实现了一个内核模块来监控给定主机的所有传入和传出数据包。通过查看 skuffs,我可以对数据包进行分类并识别出我感兴趣的数据包。每次我检测到此类数据包时,我都想生成自己的数据包并将其发送到网络中(注意:我不是复制/克隆原始数据包而是从“scratch”创建一个并使用 dev_queue_xmit 将其发送出去。与原始数据包的唯一相似之处是我的数据包去向相同的目的地,但具有不同的端口/协议(protocol)等)。

问题 是我生成的数据包在发送时负载为空。数据包成功到达目的地,有效负载的大小正确,但其内容全部设置为零。两个观察结果:(1) 我用来构造 skbuff 并发送出去的函数之前已经过测试并且似乎可以工作。 (2) 只有当我对传出数据包进行分类并尝试沿途发送自己的数据包时才会出现问题,当我使用 NF_INET_PRE_ROUTING 对数据包进行分类时,似乎相同的代码工作正常。

请看下面的代码:

static struct nf_hook_ops nfout;
static int __init init_main(void) {
        nfout.hook     = hook_func_out;
        nfout.hooknum  = NF_INET_POST_ROUTING;
        nfout.pf       = PF_INET;
        nfout.priority = NF_IP_PRI_FIRST;
        nf_register_hook(&nfout);
        printk(KERN_INFO "Loading Postrouting hook\n");
    return 0;
}
static unsigned int hook_func_out(const struct nf_hook_ops *ops,
        struct sk_buff *skb, const struct net_device *in,
        const struct net_device *out, int (*okfn)(struct sk_buff *)) {    
    if (skb is the packet that I am looking for, omitting details...) {
        generate_send_packet(skb);
    }
    return NF_ACCEPT;
}
void generate_send_packet(struct sk_buff *target_skb) {    
    static struct tcphdr * tcph;
    static struct iphdr * iph;
    static struct ethhdr * ethh;
    struct sk_buff * skb1;
    iph = ip_hdr(target_skb);
    tcph = tcp_hdr(target_skb);
    ethh = eth_hdr(target_skb);
    int payload = 123456789;    
    skb1 = construct_udp_skb(dev,
       dev->dev_addr,  mac,
       iph->saddr, iph->daddr,
       ntohs(tcph->source), dst_port,       
       (unsigned char *)&payload, sizeof(int)       
       );
    if (dev_queue_xmit(skb1) != NET_XMIT_SUCCESS) {
        printk(KERN_INFO "Sending failed\n");
    }
}

struct sk_buff* construct_udp_skb(struct net_device *dev,
    unsigned char * src_mac, unsigned char * dst_mac,
    uint32_t src_ip, uint32_t dst_ip,
    uint32_t src_port, uint32_t dst_port,
    uint32_t ttl, uint32_t tcp_seq,
    unsigned char * usr_data, uint16_t usr_data_len) {

    static struct ethhdr *ethh;
    static struct iphdr *iph;
    struct udphdr * udph;
    static struct sk_buff *skb;
    static uint16_t header_len = 300;
    unsigned char * p_usr_data;
    int udplen;


    skb = alloc_skb(1000, GFP_KERNEL);
    skb_reserve(skb, header_len);
    //-----------------------------------------------
    udph = (struct udphdr*) skb_push(skb, sizeof(struct udphdr));
    iph = (struct iphdr*) skb_push(skb, sizeof(struct iphdr));
    ethh = (struct ethhdr*) skb_push(skb, sizeof(struct ethhdr));

    memset(udph, 0 , sizeof(struct udphdr));
    memset(iph, 0 , sizeof(struct iphdr));

    skb_set_mac_header(skb, 0);
    skb_set_network_header(skb, sizeof(struct ethhdr));
    skb_set_transport_header(skb, sizeof(struct ethhdr) + sizeof(struct iphdr));

    //ETH -------------------------------------------
    memcpy(ethh->h_source, src_mac, 6);
    memcpy(ethh->h_dest, dst_mac, 6);
    ethh->h_proto = htons(ETH_P_IP);
    //IP --------------------------------------------
    iph->ihl = 5;
    iph->version = 4;
    iph->ttl = 128;
    iph->tos = 0; 
    iph->protocol = IPPROTO_UDP;
    iph->saddr = src_ip;
    iph->daddr = dst_ip;

    iph->check = ip_fast_csum((u8 *)iph, iph->ihl);
    iph->id = htons(222);
    iph->frag_off = 0;
    iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + usr_data_len );
    ip_send_check(iph);

    //UDP--------------------------------------------
    udph->source = htons(src_port);
    udph->dest = htons(dst_port);

    skb->dev = dev;
    skb->protocol = IPPROTO_UDP;
    skb->priority = 0;
    skb->pkt_type = PACKET_OUTGOING;

    p_usr_data = skb_put(skb, usr_data_len);
    printk(KERN_INFO "Sending [%i]\n", *(int*)usr_data);
    skb->csum = csum_and_copy_from_user(usr_data, p_usr_data, usr_data_len, 0, &err);


    udplen = sizeof(struct udphdr) + usr_data_len;
    udph->len = htons(udplen);
    udph->check = udp_v4_check(udplen,
                               iph->saddr, iph->daddr,
                               0);


    return skb;
}

dev_queue_xmit 会使 skbuff 中的负载无效的可能原因是什么?

谢谢!

最佳答案

我假设 printk(KERN_INFO "Sending [%i]\n", (int)usr_data) 的调试打印消息的结果符合预期。但是 skb->data 呢?能不能在csum_and_copy_from_user()之后打印一下看看是不是空的?您此时很可能已经看到零负载。

关于linux - 以零负载发送的 skbuff 数据包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41278309/

相关文章:

linux - struct load_info 中的字段strmap 是什么意思?

linux - 我可以在 Eclipse 运行配置中运行脚本吗?

c++ - 如何使用 sigsegv 捕获内存读取和写入?

c - C 内核中字符串文字的奇怪行为

c++ - 为 freebsd 11 : error: unknown type name 'choke' 编译 gcc4.8.5 时出错

c++ - 如何在 Linux 中启用 C++11

linux - 通过 Bash 脚本检索进程网络使用情况(自重启以来,如果需要)

java - 如果在 Java 中关闭套接字,是否需要关闭流?

ios - 从其他设备收到 NSData 后,NSKeyedUnarchiver 不起作用

linux - 为什么boost::aio基于epoll(synchronous)实现是异步的