Chinaunix首页 | 论坛 | 博客

分类: LINUX

2014-04-01 23:29:02

/*
   http://www.cnblogs.com/yoleung/articles/1232497.html
*/
struct sk_buff {
        /* 
           These two members must be first. 原因如下:
           next 和 prev 是两个布局字段,“内核在一个双向链表中维护着所有的sk_buff结构.”, 链表的第一个节点并非是sk_buff结构,
           而是strcut sk_buff_head 结构. 它之所以能与sk_buff一起构建链表,原因就在于头两个元素是一样的.参见:图2-1.
           问题: struct sk_buff的head字段指向的主要缓冲区(也称为线性缓冲区)的起始地址,end是指向缓冲区的结束地址.
                 在end后面进跟着skb_shared_info结构体,该结构体又有frag_list字段,指向的就是sk_buff结构. 这又是干什么使的?
           猜想: 比如应用程序的套接字调用rcv准备接收1000个字节,内核则通过对应的sock结构体找到第一个sk_buff结构,正是通过frag_list字段
                 找到了随后的sk_buff结构。这样才能快速的上报给应用程序数据.
        */
        struct sk_buff                *next;
        struct sk_buff                *prev;

        /* 
           如果该sk_buff结构由本机产生或者由本地进程接收,那么它的值是存在的.如果是被转发,为NULL.  
           "所有的网络分层都使用sk_buff结构来保存其报头和载荷" sk字段的赋值也应该由L4层来完成.本人测试过在NF_LOCAL_IN和NF_LOCALPREROUTING处获取
           sk的值,结果为NULL.
            问题:当数据包是发给本地进程时,L4层又是如何知道它属于哪个sock结构呢?应该是以protocol、src port、dst port取哈希,找到对应的sock结构.
        */
       struct sock                *sk;                

        /*
           数据包进/出时间, tstamp值可能是无效的.该值的意义在于哪里?
           1、它不能测试网络时延(可查看ping程序的测试方法). 2、 告知是由网卡驱动对
           它进行设置.所以猜测:该字段应该是为了计算在TCP/IP协议栈中处理的时间.
         */
        ktime_t                        tstamp;

        /*
           "在Linux内核中每种网路设备都用这个数据结构表示."
           比如:数据包到来时它表示数据包是从哪个设备上收上来的. 数据包发送时它表示数据包要从哪个设备上发出去.
           如果是转发呢?dev应该仍然是第一种情况. 因为skb->dst字段中包含了要从哪个网口设备发送出去的dev. 这两个dev还要比较比较,不至于从
           a网口收上来以后再从a口发出去.
        */                
        struct net_device        *dev;

        /*保存结构体 dst_entry的指针,该结构体用于保存路由查询结果. 路由信息查询函数ip_route_input,它直接将结果赋值给_skb_dst. */
        unsigned long                _skb_dst;                 
#ifdef CONFIG_XFRM
        struct        sec_path        *sp;
#endif

        /*
         * This is the control buffer. It is free to use for every
         * layer. Please put your private variables there. If you
         * want to keep them across layers you have to do a skb_clone()
         * first. This is owned by whoever has the skb queued ATM.
         */

        /*
         * 在IP层该缓冲区结构如下:#define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb)) 
         * ip_options_compile 函数会对IP报头中的IP options作解析,解析结果就存放在cb当中.
         */
        char                        cb[48];                

        /*
         len=IP报文的长度=tail-data(线性缓冲区中的长度) + frgas (支持散播/聚集时,页面缓冲区中数据的长度) + frag_list (指向的下一个sk_buff中len的大小)
         data_len = len长度的后面两部分的和,也称为分段缓冲区的长度.
         mac_len = MAC头的长度,以太网头为6+6+2
         hdr_len 通过找各种证据怀疑它有可能是:header指向的缓冲区的大小, 但是它的值不就是end-head吗?还有必要吗?
                 在skb_clone函数中发现该语句:n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
         在skb_tailroom函数中,发现如果data_len !=0 那么skb_tailroom的值为0.看来是先紧着线性缓冲区进行数据填充呢.
         */
        unsigned int                len,                
                                data_len;
        __u16                        mac_len,
                                hdr_len;

        /*
           校验和的相关字段有两个ip_summed 和 csum.分进出两种情况讨论.
           进:ip_summed表明L4校验和是否正确(正确、不正确、或者未校验). csum就应该是计算的校验和的值.
           出:csum告知网卡驱动将校验和结果放置到何处,ip_summed则告知底层是否填校验.
         */
        union {
                __wsum                csum;
                struct {
                        __u16        csum_start;
                        __u16        csum_offset;
                };
        };

        __u32                        priority;         //先不考虑.

        kmemcheck_bitfield_begin(flags1);
        __u8                        local_df:1,         //不允许本机对该skb数据分片
                                cloned:1,         /*如果调用skb_clone,则old和new sk_buff结构的cloned字段都为1*/
                                ip_summed:2,
        /*
                nohdr = 0:dataref代表整个skb数据区的引用计数
                nohdr = 1:对header buffer 
        */
                                nohdr:1,        
                                nfctinfo:3;
        __u8                        pkt_type:3,         /* 在文件include/linux/if_packet.h中定义,MAC层根据目的MAC确定pkt类型.*/
                                fclone:2,         /* fclone标志来决定从哪个缓冲池中分配SKB描述符*/
                                ipvs_property:1,
                                peeked:1,
                                nf_trace:1;
        kmemcheck_bitfield_end(flags1);
        __be16                        protocol;         /*上层协议的类型*/

        void                        (*destructor)(struct sk_buff *skb);         /*这个函数指针可以初始化成一个在缓冲区释放时完成某些动作的函数*/
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        struct nf_conntrack        *nfct;                /* 跟NETFILTER相关*/
        struct sk_buff                *nfct_reasm;        
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
        struct nf_bridge_info        *nf_bridge;         /* 在网桥过程增加NETFILTER*/
#endif

        int                        iif;                 /* 接收设备的index, 那么这跟dev字段的区别在哪里?*/
        __u16                        queue_mapping;
#ifdef CONFIG_NET_SCHED
        __u16                        tc_index;        /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
        __u16                        tc_verd;        /* traffic control verdict */
#endif
#endif


        kmemcheck_bitfield_begin(flags2);
#ifdef CONFIG_IPV6_NDISC_NODETYPE
        __u8                        ndisc_nodetype:2;
#endif
#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
        __u8                        do_not_encrypt:1;
#endif
        kmemcheck_bitfield_end(flags2);


        /* 0/13/14 bit hole */


#ifdef CONFIG_NET_DMA
        dma_cookie_t                dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
        __u32                        secmark;
#endif

        __u32                        mark;         /* packet 以sk_buff结构存在时,mark唯一标示了一个sk_buff*/

        __u16                        vlan_tci;

        sk_buff_data_t                transport_header;         /* 这些值不一定都有效,可留意一下它们被初始化的时间.*/
        sk_buff_data_t                network_header;
        sk_buff_data_t                mac_header;
        /* These elements must be at the end, see alloc_skb() for details. 以下是最重要的四个字段.  */
        sk_buff_data_t                tail;
        sk_buff_data_t                end;
        unsigned char                *head,
                                *data;
        unsigned int                truesize;        /*暂且认为是skb的大小+数据缓冲区的大小. 但是更应该是skb所占用的缓冲区大小.*/
        atomic_t                users;                 /*sk_buff被引用*/
};

阅读(1844) | 评论(0) | 转发(0) |
0

上一篇:ip_output 和 ip_finish_output

下一篇:skb_clone

给主人留下些什么吧!~~