• linux内核skb操作



    1,struct sk_buff数据结构

    struct sk_buff{
    	//这两个结构必须放在最前面
    	struct sk_buff *next;
    	struct sk_buff *prev;
    	struct sk_buff_head *list;
    	
    	struct sock *sk; //指向所属的sock结构
    	ktime_t tstamp; //表示包接收的时间
    
    	/*
    	这个变量的类型是net_device,net_device它代表一个网络设备。dev的作用与这
    	个包是准备发出的包还是刚接收的包有关。当收到一个包时,设备驱动会把sk_buff
    	的dev指针指向收到这个包的网络设备;当一个包被发送时,这个变量代表将要发
    	送这个包的设备。在发送网络包时设置这个值的代码要比接收网络包时设置这个值
    	的代码复杂。有些网络功能可以把多个网 络设备组成一个虚拟的网络设备(也就是
    	说,这些设备没有和物理设备直接关联),并由一个虚拟网络设备驱动管理。当虚拟
    	设备被使用时,dev指针指向虚拟设 备的net_device结构。而虚拟设备驱动会在一
    	组设备中选择一个设备并把dev指针修改为这个设备的net_device结构。因此,在某
    	些情况 下,指向传输设备的指针会在包处理过程中被改变。
    	*/
    	struct net_device *dev;
    	struct dst_entry *dst;
    	struct sec_path *sp;
    
    
    	union{//运输层的的头信息
    		struct tcphdr *th;
    		struct udphdr *uh;
    		struct icmphdr *icmph;
    		struct igmphdr *igmph;
    		.......	
    		unsigned char *raw;
    	}h;
    	
    	union{//网络层头信息
    		struct iphdr *iph;
    		struct ipv6hdr *ipv6h;
    		struct arphdr *arph;
    		unsigned char *raw;
    	}nh;
    
    	union{//链路层头信息
    		struct ethhdr *ethernet;
    		unsigned char *raw
    	}
    	
    	unsigned char  cloned;//是否是克隆的
    
    	char cb[48];
    
    	.......
    	//这几个变量相当重要
    	unsigned char *head; 
    	unsigned char *data; 
    	unsigned char *tail; 
    	unsigned char *end; 
    	
    	.......
    	
    }

    2,内核是如何维护sk_buff结构的,我们以一图明了:

    由上图我们可以清晰的看到内核为何sk_buff结构

    head:指针指向内存中已分配的用于存储网路数据的缓冲区起始地址,sk_buff和相关数据块在分配后,该指针的值就固定了。
    data:指针指向对应当前协议层有效数据的起始地址。每个协议的有效数据含义不同。
    tail:指向对应当前协议层有效数据负载的结尾地址,与data对应。
    end:指向内存分配的数据缓冲区的结尾地址,与head指针对应。和head一样,sk_buff和相关数据块被分配后,end指针也就固定了

    3,skb_buff的操作

    Skb操作
    1, skb_queue_empty检查队列是否为空
    原型:int skb_queue_empty(struct sk_buff_head *list)
    参数:list为队列的头
    描述:如果队列为空返回真,否则返回假


    2, skb_get引用缓冲区
    原型:struct skb_buff* skb_get(struct sk_buff *skb)
    参数:skb为要引用的缓冲区;
    描述:对套接字缓冲区引用一次,返回指向缓冲区的指针

    3, skb_free释放一个缓冲区
    原型:void kree_skb(struct sk_buff *skb)
    参数:skb为要释放的缓冲区
    描述:删除一个缓冲区的引用,如果其引用计数变为0,则释放它


    4, skb_cloned判断缓冲区是否是克隆的
    原型:int skb_cloned(struct sk_buff *skb)
    参数:skb为要检查的缓冲区
    描述:如果以skb_clone标志来产生缓冲区,则返回真,否则返回假


    5, skb_shared缓冲区是否是共享的
    原型:int skb_shared(struct sk_buff *skb)
    参数:skb为要检查的缓冲区
    描述:如果有多于一个人引用这个缓冲区就返回真


    6, skb_share_check检查缓冲区是否是共享的,如果是就克隆它
    原型:struct sk_buff *skb_share_check(struct *skb,int pri)
    参数:skb为要检查的缓冲区,pri为内存分配的优先级
    描述:如果缓冲区的共享的,就克隆这个缓冲区,并把原来缓冲区的引用计数减一,返回新克隆的缓冲区,如果不是共享的,则返回原来的缓冲区,当从中断或者全局锁调用该函数时pri必须为GFP_ATOMIC,内存分配失败返回NULL


    7, skb_unshare产生一个共享缓冲区的拷贝
    原型:struct sk_buff *skb_unshare(struct sk_buff *skb,int pri);
    参数:skb为要检查的缓冲区,pri为内存非配的优先级
    描述:如果套接字缓冲区是克隆的,那么这个函数就创建一个新的数据数据拷贝,并把原来缓冲区的引用计数减一,返回因为计数为一的新拷贝,如果不是克隆的,就返回原来的缓冲区,当从中断或者全局锁调用该函数时,pri必须是GFP_ATOMIC,内存分配错误返回NULL


    8, skb_queue_len获得队列的长度
    原型:__u32 sk_queue_len(struct sk_buff_head *list)
    参数:list为测量的链表


    9, skb_queue_head,skb_que_tail将newsk添加到链表的首部/尾部
    原型:void skb_queue_head/tail(struct sk_buff_head *list,
    struct sk_buff *newsk)
    参数:list为要使用的链表,newsk为要添加的缓冲区

    下面是几个常用的,在此贴出源代码以便更好的理解


     

    static inline void skb_reserve( struct sk_buff *skb , int len )  
    {  
    	skb->data += len ;   
    	skb->tail += len ;  
    }
    
    /**
     * skb_put - add data to a buffer
     * @skb : buffer to use
     * @len : amount of data to add
     *
     * This function extends the used data area of the buffer. If this would 
     * exceed the total buffer size the kernel will panic. A pointer to the
     * first byte of the extra data is returned.
     */
    
    unsigned char *skb_put( struct sk_buff *skb , unsigned int len )
    {
            unsigned char *tmp = skb_tail_pointer(skb) ;
            /* 如果存在非线性区,即data_len > 0 ,则报bug */
            SKB_LINEAR_ASSERT(skb) ;
            skb->tail += len ;
            skb->len += len ;
            if (unlikely(skb->tail > skb->end ))
                    skb_over_panic(skb , len , __builtin_return_address(0)) ;
            return tmp ;
    }  
    
    
    /**
     * skb_push - add data to the start of a buffer
     * @skb : buffer to use
     * @len : amount of data to add
     *
     * This function extends the used data area of the buffer at the buffer
     * start. If this would exceed the total buffer headroom the kernel will
     * panic. A pointer to the first byte of the extra data is returned.
     */
    
    unsigned char *skb_push( struct sk_buff *skb , unsigned int len )
    {
            skb->data -= len ;
            skb->len += len ;
            if ( unlikely(skb->data < skb->head ) )
                    skb_under_panic(skb , len , __builtin_return_address(0)) ;
            return skb->data ;
    }
    
    /**
     * skb_pull - remove data from the start of a buffer
     * @skb : buffer to use
     * @len : amount of data to remove
     *
     * This function removes data from the start of a buffer, returning the memory to
     * the headroom. A pointer to the next data in the buffer is returned. Once the
     * data has been pulled future pushes will overwrite the old data.
     */
    
    unsigned char *skb_pull( struct sk_buff *skb , unsigned int len )
    {
            return skb_pull_inline(skb , len ) ;
    }
    
    static inline unsigned char *skb_pull_inline(struct sk_buff *skb , unsigned int len)
    {
            return unlikely(len > skb->len ) ? NULL : __skb_pull(skb , len) ;
    }
    
    static inline unsigned char *__skb_pull(struct sk_buff *skb , unsigned int len)
    {
            skb->len -= len ;
            BUG_ON(skb->len < skb->data_len ) ;
            return skb->data += len ;
    }





     

  • 相关阅读:
    【noip2012】开车旅行
    【AC自动机】专题总结
    【noi2013】【bz3244】树的计数
    BZOJ1069: [SCOI2007]最大土地面积
    BZOJ1185: [HNOI2007]最小矩形覆盖
    BZOJ1047: [HAOI2007]理想的正方形
    BZOJ1801: [Ahoi2009]chess 中国象棋
    BZOJ1925: [Sdoi2010]地精部落
    BZOJ1057: [ZJOI2007]棋盘制作
    BZOJ1217: [HNOI2003]消防局的设立
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3202900.html
Copyright © 2020-2023  润新知