linux - 如何制作数据包的副本?

标签 linux linux-kernel packet kernel-module netfilter

我想在 Net Filter Hook 处复制数据包(并将其发送到我创建的队列)。 skb_copy 对我有用吗?我还必须在数据包之前添加 seq no,skb_reserve 会这样做吗? 我写了下面的代码来抓包

  unsigned int hook_func(unsigned int hooknum,
                         struct sk_buff **skb,
                         const struct net_device *in,
                         const struct net_device *out,
                         int (*okfn)(struct sk_buff *))
{
    if (strcmp(in->name, drop_if) == 0) {
        printk("Dropped packet on %s...\n", drop_if);

        return NF_DROP;
    } else {
        return NF_ACCEPT;
    }
}

/* Initialisation routine */
int init_module()
{
    /* Fill in our hook structure */
    nfho.hook     = hook_func;         /* Handler function */
    nfho.hooknum  = NF_IP_PRE_ROUTING; /* First hook for IPv4 */
    nfho.pf       = PF_INET;
    nfho.priority = NF_IP_PRI_FIRST;   /* Make our function first */

    nf_register_hook(&nfho);

    return 0;
}

最佳答案

我同意 Rachit Jain 的观点,除非您有正当理由在内核空间中执行此操作,否则我建议您使用 libpcap 在用户空间中执行此操作。

无论如何,如果你只是想复制数据包然后修改一些数据,我建议你分配一个有足够空间的新skb来复制你收到的skb中已有的数据+足够的空间来添加一个头。

这是我曾经使用过的代码,它不会从已经存在的 skb 中进行任何复制,但它可能对您有用。我在这里制作了一种特殊的 ICMP 回显消息

int sendICMPEcho(unsigned char *msg, unsigned int length, 
                        __be32 source, __be32 dest)
{
    struct ethhdr *eth;
    struct iphdr *iph;
    struct icmphdr *icmph;
    struct sk_buff *newPacket;
    unsigned char *data;
    unsigned int skbSize = length + sizeof(struct icmphdr)
                + sizeof(struct iphdr)
                + sizeof(struct ethhdr);
    /* Allocate the skb */
    newPacket = alloc_skb(skbSize, GFP_ATOMIC);
    if(newPacket == NULL)
        return SEND_FAIL_MEMORY;

    /* Reserve the headers area */
    skb_reserve(newPacket, sizeof(struct icmphdr)
                 + sizeof(struct iphdr) 
                + sizeof(struct ethhdr));   

    /* Extend the data area from 0 to the message length */
    data = skb_put(newPacket, length);
    /* Copy the data from the message buffer to the newPacket */
    memcpy(data, msg, length);

    /************** ICMP HEADER***************/ 
    /* skb_push - pushing the icmp header in the packet data */
    icmph = (struct icmphdr *) skb_push(newPacket,
                        sizeof(struct icmphdr));
    /*set ICMP header here */
    icmph->type = ICMP_ECHO;
    icmph->code = 100; /* Our magic number */
    icmph->un.echo.id = 0;
    icmph->un.echo.sequence = htons(sendCounter);
    icmph->checksum= 0;
    icmph->checksum = in_cksum((unsigned short *)icmph, 
                sizeof(struct icmphdr) + length);
    /************** END ICMP HEADER**************/

    /************** IP HEADER ***************/
    iph = (struct iphdr *) skb_push(newPacket,
                        sizeof(struct iphdr));
    /* set IP header here */
    iph->ihl = 5;/* 5 * 32(bits) */
    iph->version = 4;
    iph->tos = 255; /* Just a magic number - remove it */
    iph->tot_len = htons( sizeof(struct iphdr) 
                + sizeof(struct icmphdr)
                + length);
    iph->id = 0;
    iph->frag_off = 0; /* No fragementation */
    iph->ttl = 65;
    iph->protocol =  IPPROTO_ICMP;
    iph->saddr = source;
    iph->daddr = dest;
    iph->check = 0;
    iph->check = in_cksum((unsigned short *)iph, sizeof(struct iphdr));
    /************** END IP HEADER ***************/

    /*WARNING: THE CODE BELOW SHOULD BE REPLACED BY SOMETHING MORE ROBUST
        THAT USES THE KERNEL ROUTING!
        AND USES IP_LOCAL_OUT INSTEAD OF WHAT WE ARE DOING */
    /* Set up the net-device for the new packet */
/* In my code, there was a function findDeviceByIp that does the routing and return which net_device to use for transmission*/
    newPacket->dev = findDeviceByIP(source);
    if(newPacket->dev == NULL)
    {   
        kfree_skb(newPacket);
        return SEND_DEV_FAIL;
    }

    /************** ETH HEADER ***************/
    eth = (struct ethhdr *) skb_push(newPacket, sizeof(struct ethhdr));
    if(strcmp(newPacket->dev->name, "wlan0") == 0)
        memcpy(eth->h_dest, wifiMAC, 6);
    else if(strcmp(newPacket->dev->name, "eth0") == 0)
        memcpy(eth->h_dest, etherMAC, 6);
    else
    {
        kfree_skb(newPacket);
        return SEND_FAIL_SEND;
    }
    memcpy(eth->h_source, newPacket->dev->dev_addr, 6);
    eth->h_proto = htons(ETH_P_IP);
    /************** END ETH HEADER ***************/

    dev_queue_xmit(newPacket);/* Transmite the packet */
    /* END OF THE WARNING AREA */

    ++sendCounter;
    return SEND_SUCCESS;
}

关于linux - 如何制作数据包的副本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20833852/

相关文章:

linux - 什么是 libg2c 库?

linux - 如何正确观看 psql 命令?

linux - 在 amazon ec2 中执行 scp 连接时获取连接被拒绝

linux - modprobe是否自动创建一个sys文件接口(interface)-/sys/module/?

c++ - 通过 Boost Asio 获取 UDP 数据报长度?

Python Twisted 代理 - 如何拦截数据包

python - 没有收到所有 ICMP 超时消息 : why?

linux - 如何找出docker镜像中安装了哪个Linux?

linux-kernel - TIF_NEED_RESCHED 有什么作用?

c - 从内核空间访问数据段