欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

内核扩展数据包的方法之-----skb_copy_expand

程序员文章站 2024-03-20 10:46:28
...

skb_copy_expand是用来拷贝并且扩展原来skb的一个函数。
方法的作用很简单,那么接下来,我们一起看看skb_copy_expand方法的实现方式以及用法。

skb_copy_expand原函数


/**
  * skb_copy_expand - 复制并扩展sk_buff
  * @skb:要复制的缓冲区
  * @newheadroom:头上有新的空闲字节
  * @newtailroom:尾部新的空闲字节
  * @gfp_mask:分配优先级
  *复制一个&sk_buff及其数据,同时分配额外的空间。
  *当调用者希望修改数据并需要数据的私有副本以及新字段的更多空间时使用此选项。在成功时返回新的skb,失败时返回NULL。 返回的缓冲区的引用计数为1。
  *如果从中断调用此函数,则必须通过GFP_ATOMIC作为分配优先级。
 */

struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
                int newheadroom, int newtailroom,
                gfp_t gfp_mask)
{
    /*分配复制缓冲区*/
    struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom,
                    gfp_mask, skb_alloc_rx_flag(skb),
                    NUMA_NO_NODE);

    /*计算sk_buff头部的可用空间的字节数*/
    int oldheadroom = skb_headroom(skb);
    int head_copy_len, head_copy_off;
    int off;

    if (!n)
        return NULL;

    /*给头部预留新的空间*/
    skb_reserve(n, newheadroom);

    /* 设置尾指针和数据包的长度 */
    skb_put(n, skb->len);

    head_copy_len = oldheadroom;
    head_copy_off = 0;
    if (newheadroom <= head_copy_len)
        head_copy_len = newheadroom;
    else
        head_copy_off = newheadroom - head_copy_len;

    /*复制预留头部之后的数据 */
    if (skb_copy_bits(skb, -head_copy_len, n->head + head_copy_off,
              skb->len + head_copy_len))
        BUG();
    /*拷贝原skb的一些指针数据*/
    copy_skb_header(n, skb);

    off = newheadroom - oldheadroom;
    if (n->ip_summed == CHECKSUM_PARTIAL)
        n->csum_start += off;
    return n;
}

从上面代码我们可以看到skb_copy_expand函数首先重新分配了一块新的内存用于存储原来的数据和将要扩展的数据,然后将原来的数据包重新进行偏移拷贝等动作,将原有的数据包拷贝到新的数据包之中,如果需要扩展数据则可以将数据扩展到新数据包的空余内存中。

使用skb_copy_expand扩展dhcp数据包的options字段

int test(struct sk_buff *skb)
{   
    int newlen, movelen;
    struct dhcp_options_info dhcp_opt_info = {0};
    unsigned char dataadd[20] = {110, 0x01, 'a'};
    unsigned char value;
    unsigned char *p;
    unsigned char *udpdata, *dhcp_pck_type_location, *dhcp_option_110_location;
    struct sk_buff *dhcp_skb;

    struct iphdr *iph1;
    struct udphdr *udph1;

    struct ether_header *ether_hdr = (struct ether_header *)(skb->mac_header);

    if(ether_hdr->ether_type != htons(ETH_P_IP)) 
        return 0;

    struct iphdr *iph = (struct iphdr *)ip_hdr(skb);

    if(iph && iph->protocol != IPPROTO_UDP) 
        return 0;

    //ip header --> iph->ihl*4
    struct udphdr *udph = (struct udphdr *)((char *)iph + iph->ihl*4);
    if(udph && udph->source == htons(68) && udph->dest== htons(67))
    {   
        udpdata = (char *)udph + 8;//  point to udp data
        dhcp_pck_type_location   = (char *)udpdata + 242;
        dhcp_option_110_location = (char *)udpdata + 243;
        if((udpdata[242] == 0x01 
            || udpdata[242] == 0x03) 
            && udpdata[243] != 110)
        { 
            /*构造需要插入新的数据*/
            if(0 == strcmp(skb->dev->name,"wlan1-va0")
              || 0 == strcmp(skb->dev->name,"wlan0-va0")){
                value = 'g';
            }else{
                value = 'h';
            }

            memcpy(dataadd + 2, &value, sizeof(value));
            dataadd[2 + 1 + 1] ='\0';
            newlen = 2 + sizeof(value);
            dataadd[1] = sizeof(value);
            /*重新构造扩展报文*/
            dhcp_skb = skb_copy_expand(skb, skb_headroom(skb), skb_tailroom(skb)+newlen,GFP_ATOMIC);  
            if(!dhcp_skb)  
            {   
                return 0;  
            }     
            /*获取新的ip头部*/
            iph1 = (struct iphdr *)ip_hdr(dhcp_skb);
            /*获取新的 udp 头部*/
            udph1 = (struct udphdr *)((char *)iph1 + iph1->ihl*4);


            udpdata = (char *)udph1 + 8;//  point to udp data
            dhcp_pck_type_location   = (char *)udpdata + 242;
            dhcp_option_110_location = (char *)udpdata + 243;

            /*增长数据包的总长度*/
            skb_put(dhcp_skb, newlen);
            /*这里要注意数据的大小端*/         
            iph1->tot_len = htons(ntohs(iph1->tot_len) + newlen);
            udph1->len = htons(ntohs(udph1->len) + newlen);

            /*设置插入新的数据*/
            movelen = ntohs(udph1->len) - 8 - 243 - newlen;
            memmove(dhcp_option_110_location + newlen, dhcp_option_110_location, movelen);
            memcpy(dhcp_option_110_location , dataadd, newlen);

            /*重新计算新的 checksum值*/
            udph1->check = 0;
            dhcp_skb->csum = skb_checksum (dhcp_skb, iph1->ihl*4, dhcp_skb->len - iph1->ihl * 4, 0);
            udph1->check = csum_tcpudp_magic(iph1->saddr, iph1->daddr, dhcp_skb->len - iph1->ihl * 4,         IPPROTO_UDP, dhcp_skb->csum);
            iph1->check = 0;
            ip_send_check (iph1);


           /*重新入内核队列*/  
           netif_rx(dhcp_skb);
           /*释放原来的skb,  这里注意skb释放的位置,不一定必须在这里释放*/
           kfree_skb(skb);
           return 1;
        }
    }
    return 0;
}

内核扩展数据包的方法之-----skb_copy_expand
上面的例子是将数据插入到扩展数据包的数据中间,具体的数据包的数据该怎么扩展可以根据需求而定