博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux网络内核结构
阅读量:4031 次
发布时间:2019-05-24

本文共 6626 字,大约阅读时间需要 22 分钟。

------------------------------------------------------------------------------------------

|----------------------------------| 

          应用层

|----------------------------------|

      BSD Socket层

|----------------------------------| 

       Inet Socket层

|----------------------------------|

       IP层

|----------------------------------|

   数据链路/硬件层

|----------------------------------|

 

IP层:           IP协议栈的实现,完成路由的查找过程(主要处理skb)

 

Inet Socket层: 对IP包进行分组排序,实现QoS,传输层协议TCP/UDP协议栈的实现

           使用sock{}类型数据来管理会话,数据主要放在sk_buff结构中   

 

BSD Socket:      对于BSD Socket相关调用的实现,主要使用socket{}结构来存放连接

                  数据主要是放在msghdr{}结构中

 

 

msghdr 结构

 

struct msghdr {

             void *           msg_name;    

             int              msg_namelen;

             struct iovec *   msg_iov;

             __kernel_size_t msg_iovlen;   

             void             *msg_control; 

            __kernel_size_t       msg_controllen;    

            unsigned              msg_flags;

};

 

msg_name:   socket的名字,通常用NULL初始化

msg_iov:     发送或接收缓冲区地址数据 

msg_iovlen:   msg_iov中保存的数据包的个数

msg_flags:    保存数据接收时的控制标志,可以由应用

       层控制,如果设置为MSG_DONTWAIT,则表示使用非阻塞方式收取数据包

 

struct iovec

{

        void __user *iov_base;  

        __kernel_size_t iov_len;

};

 

实际上,msghdr中存储的iovec是一个数组,每个iovec都有自己单独的数据起始地址和

数据长度,以下的代码将skb->data(kdata)用copy_to_user传到用户态,用户态的BSD

Socket接口将分段的数据进行合并,组成一段连续的内存buffer.

 

int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)

{

        while (len > 0) {

               if (iov->iov_len) {

                      int copy = min_t(unsigned int, iov->iov_len, len);

                      if (copy_to_user(iov->iov_base, kdata, copy))

                             return -EFAULT;

                      kdata += copy;

                      len -= copy;

                      iov->iov_len -= copy;

                      iov->iov_base += copy;

               }

               iov++;

        }

 

        return 0;

}

 

但是,我发现linux2.6.23中出现了新的函数dma_memcpy_to_iovec,具体功能有可能是

通过DMA控制器将数据包从内核拷贝到用户态,这将会对网络收包的效率有一定的改善.

linux内核2.6.14版本却没有相关的代码支持,很可能是一个新的标准出现了.

 

关于SKB

 

SKB结构中最大的变化莫过于将union舍弃,也就是说,由

     union {

         struct tcphdr     *th;

         struct udphdr     *uh;

         struct icmphdr     *icmph;

         struct igmphdr     *igmph;

         struct iphdr     *ipiph;

         struct ipv6hdr     *ipv6h;

         unsigned char     *raw;

     } h;

 

     union {

         struct iphdr     *iph;

         struct ipv6hdr     *ipv6h;

         struct arphdr     *arph;

         unsigned char     *raw;

     } nh;

 

     union {

           unsigned char      *raw;

     } mac;

 

 

变成了

  sk_buff_data_t         transport_header;

     sk_buff_data_t         network_header;

     sk_buff_data_t         mac_header;

 

 

结构变得更加的简单了,对于获取IP头的操作,发生了如下

变化

 

在linux 2.4~linux 2.6.20下:

struct iphdr *ip = skb->nh.iph;

 

到了linux 2.6.22下,就变成了:

struct iphdr *ip = ip_hdr(skb);

 

这里是函数ip_hdr的定义,调用了skb_network_header

函数用以获取IP头:

 

static inline struct iphdr *ip_hdr(const struct sk_buff *skb)

{

        return (struct iphdr *)skb_network_header(skb);

}

 

但是函数skb_network_header又有不同的实现,依赖于宏NET_SKBUFF_DATA_USES_OFFSET是否进行了定义

 

#if BITS_PER_LONG > 32

#define NET_SKBUFF_DATA_USES_OFFSET 1

#endif

 

这里完成了NET_SKBUFF_DATA_USES_OFFSET宏的定义,其实很简单,只是看目前

的系统是32位系统还是64位系统

 

#ifdef NET_SKBUFF_DATA_USES_OFFSET

 

static inline unsigned char *skb_transport_header(const struct sk_buff *skb)

{

        return skb->head + skb->transport_header;

}

#else

static inline unsigned char *skb_network_header(const struct sk_buff *skb)

{

        return skb->network_header;

}

 

 

以上是*skb_network_header函数的实现,可以看到,对于32位系统和64位系统,skb_network_header

函数有不同的实现,所以在编程中,直接调用ip_hdr函数是好的,而不要直接通过skb->network_header

获取IP Header

 

对于传输层协议,也应该调用tcp_hdr或者udp_hdr来进行解析,而不要调用skb中的transport_header

 

static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb)

{

        return (struct tcphdr *)skb_transport_header(skb);

}

 

static inline struct udphdr *udp_hdr(const struct sk_buff *skb)

{

        return (struct udphdr *)skb_transport_header(skb);

}

 

#ifdef NET_SKBUFF_DATA_USES_OFFSET

static inline unsigned char *skb_transport_header(const struct sk_buff *skb)

{

        return skb->head + skb->transport_header;

}

#else

static inline unsigned char *skb_transport_header(const struct sk_buff *skb)

{

        return skb->transport_header;

}

 

如果tcph没有解析出来的话,依然可以用 ip_hdr(skb)->ihl*4找到tcp header的偏移

 

skb 常用的操作

 

实际上在head和data,tail和end之间,存在着空洞,所以SKB可以向上或者向下进行扩展

skb_put() 将数据添加到现有数据的尾部

skb_push() 将数据添加到现有数据的头部

skb_pull() 从数据头部开始缩减

skb_trim() 从数据尾部开始缩减

 

socket 结构

 

socket结构主要用于BSD Socket层存储连接信息,应用程序需要一个文件描述符来和socket结构

进行对应

 

struct socket {

        socket_state            state;

        unsigned long          flags;

        const struct proto_ops    *ops;

        struct fasync_struct       *fasync_list;

        struct file         *file;

        struct sock             *sk;

        wait_queue_head_t wait;

        short                      type;

};

 

这个结构在2.6.14和2.6.22中没有变化

 

socket_state描述了连接的状态,可以是以下枚举类型

 

typedef enum {

        SS_FREE = 0,                    

        SS_UNCONNECTED,                

        SS_CONNECTING,                   

        SS_CONNECTED,              

        SS_DISCONNECTING       

} socket_state;

 

flags:用户层的一些控制信息

 

在2.4内核中,使用一个inode结构来进行文件描述符和一个socket之间的对应,所以在socket

结构中有一个inode成员,2.6内核中舍弃了这一成员,在socket创建函数sock_alloc中,使用

SOCKET_I函数根据新生成的inode结构返回相对应的socket结构,以后inode结构就再也用不着了.

 

file:用于垃圾收集

 

sk:指定了Inet socket层中sock结构的指针,同时在sock结构中也包含了socket函数的指针,

    所以sock和socket结构是一一对应的关系

 

 

sock结构

struct sock {

 

        struct sock_common      __sk_common;

        #define sk_family           __sk_common.skc_family

        #define sk_state             __sk_common.skc_state

        #define sk_reuse            __sk_common.skc_reuse

        #define sk_bound_dev_if               __sk_common.skc_bound_dev_if

        #define sk_node                    __sk_common.skc_node

        #define sk_bind_node            __sk_common.skc_bind_node

        #define sk_refcnt           __sk_common.skc_refcnt

        #define sk_hash                    __sk_common.skc_hash

        #define sk_prot                     __sk_common.skc_prot

        unsigned char         sk_shutdown : 2,

                             sk_no_check : 2,

                             sk_userlocks : 4;

        unsigned char         sk_protocol;

        unsigned short               sk_type;

        int                   sk_rcvbuf;

        socket_lock_t         sk_lock;

 

        struct {

               struct sk_buff *head;

               struct sk_buff *tail;

        } sk_backlog;

        wait_queue_head_t *sk_sleep;

        struct dst_entry       *sk_dst_cache;

        struct xfrm_policy   *sk_policy[2];

        rwlock_t         sk_dst_lock;

        atomic_t          sk_rmem_alloc;

        atomic_t          sk_wmem_alloc;

        atomic_t          sk_omem_alloc;

        int                   sk_sndbuf;

        struct sk_buff_head        sk_receive_queue;

        struct sk_buff_head        sk_write_queue;

        struct sk_buff_head        sk_async_wait_queue;

        int                   sk_wmem_queued;

        int                   sk_forward_alloc;

        gfp_t                      sk_allocation;

        int                   sk_route_caps;

        int                   sk_gso_type;

        int                   sk_rcvlowat;

        unsigned long                sk_flags;

        unsigned long            sk_lingertime;

        struct sk_buff_head        sk_error_queue;

        struct proto            *sk_prot_creator;

        rwlock_t         sk_callback_lock;

        int                   sk_err,

                             sk_err_soft;

        unsigned short               sk_ack_backlog;

        unsigned short               sk_max_ack_backlog;

        __u32                    sk_priority;

        struct ucred            sk_peercred;

        long                sk_rcvtimeo;

        long                sk_sndtimeo;

        struct sk_filter            *sk_filter;

        void                *sk_protinfo;

        struct timer_list       sk_timer;

        ktime_t                   sk_stamp;

        struct socket           *sk_socket;

        void                *sk_user_data;

        struct page             *sk_sndmsg_page;

        struct sk_buff         *sk_send_head;

        __u32                    sk_sndmsg_off;

        int                   sk_write_pending;

        void                *sk_security;

        void                (*sk_state_change)(struct sock *sk);

        void                (*sk_data_ready)(struct sock *sk, int bytes);

        void                (*sk_write_space)(struct sock *sk);

        void                (*sk_error_report)(struct sock *sk);

        int                   (*sk_backlog_rcv)(struct sock *sk,

                                              struct sk_buff *skb); 

        void                     (*sk_destruct)(struct sock *sk);

};

 

sk_rcvbuf:接收缓冲区的长度限制

sk_sndbuf:发送缓冲区的长度限制

sk_rmem_alloc:已经申请的接收缓冲区长度

sk_wmem_alloc:已经申请的发送缓冲区长度

sk_receive_queue:等待接收的数据包队列头

sk_write_queue:等待发送的数据包队列头

sk_forward_alloc:为这个sock结构提前申请的数据区大小

sk_omem_alloc:当前申请的sock选项的空间大小

sk_dst_cache:dst_entry结构的链表,作为路由地址链表

sk_filter:这个结构比较有趣,关联到内核中BPF的实现,BPF规则将通过这一结构进行

     下发

 

转载地址:http://dwhbi.baihongyu.com/

你可能感兴趣的文章
小谈python 输出
查看>>
Django objects.all()、objects.get()与objects.filter()之间的区别介绍
查看>>
python:如何将excel文件转化成CSV格式
查看>>
Django 的Error: [Errno 10013]错误
查看>>
机器学习实战之决策树(一)
查看>>
[LeetCode By Python] 2 Add Two Number
查看>>
python 中的 if __name__=='__main__' 作用
查看>>
机器学习实战之决策树二
查看>>
[LeetCode By Python]7 Reverse Integer
查看>>
[LeetCode By Python]9. Palindrome Number
查看>>
[leetCode By Python] 14. Longest Common Prefix
查看>>
[LeetCode By Python]107. Binary Tree Level Order Traversal II
查看>>
[LeetCode By Python]108. Convert Sorted Array to Binary Search Tree
查看>>
[leetCode By Python]111. Minimum Depth of Binary Tree
查看>>
[LeetCode By Python]118. Pascal's Triangle
查看>>
[LeetCode By Python]121. Best Time to Buy and Sell Stock
查看>>
[LeetCode By Python]122. Best Time to Buy and Sell Stock II
查看>>
[LeetCode By Python]125. Valid Palindrome
查看>>
[LeetCode By Python]136. Single Number
查看>>
[LeetCode By Python]167. Two Sum II - Input array is sorted
查看>>