1. 程式人生 > >核心擴充套件資料包的方法之-----skb_copy_expand

核心擴充套件資料包的方法之-----skb_copy_expand

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;
}

options 110欄位新增成功
上面的例子是將資料插入到擴充套件資料包的資料中間,具體的資料包的資料該怎麼擴充套件可以根據需求而定