使用零有效载荷发送的skbuff数据包

时间:2016-12-22 07:58:56

标签: linux networking kernel netfilter

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

问题是我生成的数据包是以空载荷发送的。数据包成功到达目的地,有效负载的大小正确,但其内容全部设置为零。两个观察结果:(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中的有效负载无效,这可能是什么原因?

谢谢!

1 个答案:

答案 0 :(得分:0)

我假设printk的调试打印消息的结果(KERN_INFO“发送[%i] \ n”,(int )usr_data)是预期的。但是skb->数据怎么样?您可以尝试在csum_and_copy_from_user()之后打印它以查看它是否为空?看来你很可能已经在这一点上看到了零载荷。