Linux 网络子系统

今天记录一下Linux网络子系统相关的东西。

因为感觉对这一块还是有一个很大的空白,这件事情太可怕了。

摘抄多份博客进行总结一下Linux网络子系统的相关东西。

一. Linux网络子系统体系结构

  Linux 网络体系结构由如下图抽象的形容一下

1 . 用户空间:-----> 应用层

2 . 内核空间:-----> 系统调用接口: 主要指socket 系统调用

       -----> 协议无关接口: 实现一组基于socket的通用函数访问各种不同的协议

       -----> 网络协议:   udp, tcp 协议

       -----> 设备无关接口: 将协议与各种网络设备驱动连接在一起。

       -----> 设备驱动:   网络体系结构的最底层部分是负责管理物理网络设备的设备驱动层

3 . 硬件: 网络物理设备。

    从上往下,形成Linux网络子系统。

二. 重要的头文件以及数据结构介绍

    重要的头文件:include/linux/netdevice.h

    这里面有个网络子系统的核心数据结构:net_device

net_device

        这里面包含了网络设备的各种属性,主要有如下重要属性:
struct net_device
{
     char   name[IFNAMSIZ];
     // 这是设备的名称,包含一个%d格式串,设备注册时,
     // 用一个数替换它形成一个新的名称,分配的编号从零开始
                    
     //...
     unsigned long       state;
     //记录了设备的状态,是否激活是否是有效是否没有载波等等

     unsigned int        irq;
     /* device IRQ number  中断号  */ 
     unsigned int        mtu;
     /* interface MTU value  最大传输单元,默认1500   */ 
               unsigned char       *dev_addr;  
     /* hw address, (before bcast                
               because most packets are                             
               unicast) */       
     //......还有很多相关的属性。       
     //关于网络设备的操作结构体
     const struct net_device_ops *netdev_ops;
     const struct ethtool_ops *ethtool_ops;
     
}
#define alloc_netdev(sizeof_priv, name, setup) 
          alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)
         /*  分配一个net_device结构体可以用上面这个宏函数进行分配   
                其实最终调用是调用了alloc_netdev_mqs 函数*/
         /*  其中,参数sizeof_priv 表示网卡设备的私有数据大小,
                name指定网卡设备名称,setup指定网卡设备初始化回调函数  */ 
   

//对于不同的网卡设备,kernel提供了更直接的分配函数,
//例如以太网卡,可用用下面的宏函数进行分配:
//include/linux/etherdevice.h     
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)           
#define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
//其实最终调用是alloc_etherdev_mqs


//....
//其实这个函数只是做了简单的封装  net/ethernet/eth.c
struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,       
                      unsigned int rxqs)                                        
{                                                                               
    return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);     
}                                                                               
EXPORT_SYMBOL(alloc_etherdev_mqs);                                              
//最终调用还是alloc_netdev_mqs 只不过名字是eth开头,%d会在后面解析出来。


//.......
//ether_setup 函数,以太网初始化函数    net/ethernet/eth.c
/**                                                                             
 * ether_setup - setup Ethernet network device                                  
 * @dev: network device                                                         
 * Fill in the fields of the device structure with Ethernet-generic values.     
 */                                                                           
void ether_setup(struct net_device *dev)                                        
{                                                                               
    dev->header_ops     = &eth_header_ops;                                      
    dev->type       = ARPHRD_ETHER;                                             
    dev->hard_header_len    = ETH_HLEN;                                         
    dev->mtu        = ETH_DATA_LEN;                                             
    dev->addr_len       = ETH_ALEN;                                             
    dev->tx_queue_len   = 1000; /* Ethernet wants good queues */                
    dev->flags      = IFF_BROADCAST|IFF_MULTICAST;                              
    dev->priv_flags     |= IFF_TX_SKB_SHARING;                                  
                                                                                
    memset(dev->broadcast, 0xFF, ETH_ALEN);                                     
                                                                                
}                                                                               
EXPORT_SYMBOL(ether_setup);                                                     





struct net_device_ops

    和字符设备有一定的相似之处,网络设备也有相关的操作函数。
    他包含在net_device 结构体中的struct net_device_ops。
    他的原型如下:
struct net_device_ops {                                                         
    int         (*ndo_init)(struct net_device *dev);
                /* 该函数将在设备注册时,
                调用(即调用register_netdev()时)来做一些基本的初始化工作。 */                            
    void        (*ndo_uninit)(struct net_device *dev);                      
    int         (*ndo_open)(struct net_device *dev);
                /* 该函数在网络设备被激活变为up状态时被调用  */                            
    int         (*ndo_stop)(struct net_device *dev);
                /* 变为down状态时被调用  */                            
    netdev_tx_t     (*ndo_start_xmit) (struct sk_buff *skb,                     
                           struct net_device *dev);
                    /* 当上层协议需要发送数据时,调用该函数
                    需要返回NETDEV_TX_OK 或NET_TX_BUSY来确认结果 */                             
    u16             (*ndo_select_queue)(struct net_device *dev,                     
                            struct sk_buff *skb);  
                    /* 呼叫然后决定哪个队列当设备支持多重传输队列的时候 */                             
    void            (*ndo_change_rx_flags)(struct net_device *dev,              
                               int flags);
                    /* 调用这个函数允许设备接收器更改配置当运行多路传输的时候  */                                      
    void            (*ndo_set_rx_mode)(struct net_device *dev);
                    /* 当调用这个函数改变地址过滤
                    如果驱动没有设置地址过滤,那个这个标志要设置为IFF_UNICAST_FLT    */
    int             (*ndo_set_mac_address)(struct net_device *dev,                  
                               void *addr);
                    /* 调用设置MAC地址  */                                     
    int             (*ndo_validate_addr)(struct net_device *dev);
                    /* 测试多媒体访问地址是否有效   */                 
    int             (*ndo_do_ioctl)(struct net_device *dev,                         
                            struct ifreq *ifr, int cmd);  
                    /* 当用户请求一个ioctl,如果ioctl没有相对应的接口函数,则返回not supported error code    */                     
    int             (*ndo_set_config)(struct net_device *dev,                       
                              struct ifmap *map);                               
                    /* 用它设置网络设备总线接口参数
                    保留这个接口的原因是因为新的设备要用PCI为了低水平的管理*/
    int             (*ndo_change_mtu)(struct net_device *dev,                       
                          int new_mtu); 
                    /*  使用这个函数该表最大传输单元,
                    如果没有定义,那么任何请求改变MTU的请求都会报错 */         
    int             (*ndo_neigh_setup)(struct net_device *dev,                      
                           struct neigh_parms *);
                                                   
    void            (*ndo_tx_timeout) (struct net_device *dev);                 
                    /* 当传送器没有任何操作超时调用   */                                                           
    struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev,        
                             struct rtnl_link_stats64 *storage);                
    struct net_device_stats* (*ndo_get_stats)(struct net_device *dev); 
                    /*  上面两个函数必须定义其中一个,这是用户想获取帮助的所调用的 */         
                                                                                
    void            (*ndo_vlan_rx_add_vid)(struct net_device *dev,              
                               unsigned short vid);  
                    /* 当设备支持VLAN过滤的时候,这个函数在一个VLAN ID 被注册时候调用  */       
    void            (*ndo_vlan_rx_kill_vid)(struct net_device *dev,             
                                unsigned short vid);                     
                    /* 当设备支持VLAN过滤的时候,这个函数在一个VLAN ID 被注销时候调用   */       
#ifdef CONFIG_NET_POLL_CONTROLLER                                       
    void            (*ndo_poll_controller)(struct net_device *dev);
                      
    int             (*ndo_netpoll_setup)(struct net_device *dev,                    
                             struct netpoll_info *info);                        
    void            (*ndo_netpoll_cleanup)(struct net_device *dev);             
#endif                  
                /* SR-IOV management functions */                                                           
    int         (*ndo_set_vf_mac)(struct net_device *dev,                       
                          int queue, u8 *mac);                                  
    int         (*ndo_set_vf_vlan)(struct net_device *dev,                      
                           int queue, u16 vlan, u8 qos);                        
    int         (*ndo_set_vf_tx_rate)(struct net_device *dev,                   
                              int vf, int rate);                                
    int         (*ndo_set_vf_spoofchk)(struct net_device *dev,                  
                               int vf, bool setting);                           
    int         (*ndo_get_vf_config)(struct net_device *dev,                    
                             int vf,                                            
                             struct ifla_vf_info *ivf);                         
    int         (*ndo_set_vf_port)(struct net_device *dev,                      
                           int vf,                                              
                           struct nlattr *port[]);                              
    int         (*ndo_get_vf_port)(struct net_device *dev,                      
                           int vf, struct sk_buff *skb);                        
    int         (*ndo_setup_tc)(struct net_device *dev, u8 tc); 
                /*  调用这个函数设置tc 数字关于net设备的传输类别 
                        这个允许网络设备安全的管理队列  */                
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)                         
    int         (*ndo_fcoe_enable)(struct net_device *dev); 
                /*  当FCoE协议栈使用LLD,所以下面的设备需要配置或者初始化关于FCoE传输的加速。
                    这时就调用这个函数  */                    
    int         (*ndo_fcoe_disable)(struct net_device *dev);
                /*  与上面那个函数刚好相反  */
                /*   .........*/
    int         (*ndo_fcoe_ddp_setup)(struct net_device *dev,                   
                              u16 xid,                                          
                              struct scatterlist *sgl , unsigned int sgc);
                /*  当FCoE初始化想初始一个IO因为有个可能的候选人为直接的数据放置,成功在这个IO执行PPD这个函数返回1, 失败返回0  */                          
    int         (*ndo_fcoe_ddp_done)(struct net_device *dev,                    
                             u16 xid);          
                /*  当FCoE目标已经完成,这时候要释放这个资源以便下次请求这个资源   */              
    int         (*ndo_fcoe_ddp_target)(struct net_device *dev,                  
                               u16 xid,                                         
                               struct scatterlist *sgl,                         
                               unsigned int sgc);                               

#endif                                                                          
                                                                                
#if defined(CONFIG_LIBFCOE) || defined(CONFIG_LIBFCOE_MODULE)                   
#define NETDEV_FCOE_WWNN 0                                                      
#define NETDEV_FCOE_WWPN 1                                                      
    int         (*ndo_fcoe_get_wwn)(struct net_device *dev,                     
                            u64 *wwn, int type);                                
#endif                                                                          
                                                                                
#ifdef CONFIG_RFS_ACCEL                                                         
    int         (*ndo_rx_flow_steer)(struct net_device *dev,                    
                             const struct sk_buff *skb,                         
                             u16 rxq_index,                                     
                             u32 flow_id); 
                 /* 设置硬件过滤为RFS */                                     
#endif                                                                          
    int         (*ndo_add_slave)(struct net_device *dev,                        
                         struct net_device *slave_dev);
                 /* 添加一个网络设备的从设备 */        
    int         (*ndo_del_slave)(struct net_device *dev,                        
                         struct net_device *slave_dev);
                 /* 删除  */                         
    u32         (*ndo_fix_features)(struct net_device *dev,                     
                            u32 features);
                 /* 调整请求特征标志通过设备专用的 约束,返回标志结果,不能改变设备的状态*/                                      
    int         (*ndo_set_features)(struct net_device *dev,                     
                            u32 features);  
                 /* 升级设备新的配置   */                                    
};                                                                              

                              

sk_buff

    sk_buff 在include/linux/skbuff.h 里面被声明
/**                                                                             
 *  struct sk_buff - socket buffer                                              
 *  @next: Next buffer in list                                                  
 *  @prev: Previous buffer in list                                              
 *  @tstamp: Time we arrived                                                    
 *  @sk: Socket we are owned by                                                 
 *  @dev: Device we arrived on/are leaving by                                   
 *  @cb: Control buffer. Free for use by every layer. Put private vars here     
 *  @_skb_refdst: destination entry (with norefcount bit)                       
 *  @sp: the security path, used for xfrm                                       
 *  @len: Length of actual data                                                 
 *  @data_len: Data length                                                      
 *  @mac_len: Length of link layer header                                       
 *  @hdr_len: writable header length of cloned skb                              
 *  @csum: Checksum (must include start/offset pair)                            
 *  @csum_start: Offset from skb->head where checksumming should start          
 *  @csum_offset: Offset from csum_start where checksum should be stored        
 *  @priority: Packet queueing priority                                         
 *  @local_df: allow local fragmentation                                        
 *  @cloned: Head may be cloned (check refcnt to be sure)                       
 *  @ip_summed: Driver fed us an IP checksum                                    
 *  @nohdr: Payload reference only, must not modify header                      
 *  @nfctinfo: Relationship of this skb to the connection                       
 *  @pkt_type: Packet class                                                     
 *  @fclone: skbuff clone status                                                
 *  @ipvs_property: skbuff is owned by ipvs                                     
 *  @peeked: this packet has been seen already, so stats have been              
 *      done for it, don't do them again                                        
 *  @nf_trace: netfilter packet trace flag                                      
 *  @protocol: Packet protocol from driver                                      
 *  @destructor: Destruct function                                              
 *  @nfct: Associated connection, if any                                        
 *  @nfct_reasm: netfilter conntrack re-assembly pointer                        
 *  @nf_bridge: Saved data about a bridged frame - see br_netfilter.c           
 *  @skb_iif: ifindex of device we arrived on                                   
 *  @tc_index: Traffic control index                                            
 *  @tc_verd: traffic control verdict                                           
 *  @rxhash: the packet hash computed on receive                                
 *  @queue_mapping: Queue mapping for multiqueue devices                        
 *  @ndisc_nodetype: router type (from link layer)                              
 *  @ooo_okay: allow the mapping of a socket to a queue to be changed           
  *  @ndisc_nodetype: router type (from link layer)                              
 *  @ooo_okay: allow the mapping of a socket to a queue to be changed           
 *  @l4_rxhash: indicate rxhash is a canonical 4-tuple hash over transport      
 *      ports.                                                                  
 *  @dma_cookie: a cookie to one of several possible DMA operations             
 *      done by skb DMA functions                                               
 *  @secmark: security marking                                                  
 *  @mark: Generic packet mark                                                  
 *  @dropcount: total number of sk_receive_queue overflows                      
 *  @vlan_tci: vlan tag control information                                     
 *  @transport_header: Transport layer header                                   
 *  @network_header: Network layer header                                       
 *  @mac_header: Link layer header                                              
 *  @tail: Tail pointer                                                         
 *  @end: End pointer                                                           
 *  @head: Head of buffer                                                       
 *  @data: Data head pointer                                                    
 *  @truesize: Buffer size                                                      
 *  @users: User count - see {datagram,tcp}.c                                   
 */

//这个结构体里面所有的成员如上注释 
//其中重要的有如下几个着重解释一下:
struct sk_buff 
{                                                                
    /* These two members must be first. */                                      
    struct sk_buff      *next;         /* 指向下一个的sk_buff  */                                         
    struct sk_buff      *prev;         /* 指向前一个的sk_buff  */                                  

    struct sock *sk;                   /*  该sk_buff 拥有的套接字   */
    struct net_device  *dev;           /*  处理该包的设备 */
    
    __be16          protocol;          /*  该包所属的协议类型  */
    unsigned int        len,                                                    
                data_len;              /*  有效数据的长度以及数据总长度   */
    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;                         /* 有效数据的开始 */                                                 
                                                                                  
}
//sk_buff中定义了4个指向数据包缓冲区不同位置的指针head、data、tail、end、
//.......
//sk_buff 的基本操作函数

static inline struct sk_buff *__dev_alloc_skb(unsigned int length,   <-------------|          
                          gfp_t gfp_mask)                                          | 
{                                                                                  | 
    struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask);  ---          | 
    if (likely(skb))                                                    |          | 
        skb_reserve(skb, NET_SKB_PAD);                                  |          | 
    return skb;                                                         |          | 
}                                                                       |          | 
                                                                        |          | 
extern struct sk_buff *dev_alloc_skb(unsigned int length);              |          |     
/*                                                                      |          |               
struct sk_buff *dev_alloc_skb(unsigned int length)     //最开始位置     |          | 
{                                                                       |          |                                     
     * There is more code here than it seems:                           |          | 
     * __dev_alloc_skb is an inline                                     |          |                               
     */                                                                 |          |                   
    return __dev_alloc_skb(length, GFP_ATOMIC);    --------------------------------                     |                                     
}                                                                       |                                                                         
EXPORT_SYMBOL(dev_alloc_skb);                                           |          
*/              |                                                       |   
                                                                        |         
extern struct sk_buff *__netdev_alloc_skb(struct net_device *dev,       |         
        unsigned int length, gfp_t gfp_mask);                           |         
                                                                        | 
                                                                        | 
                                                                        | 
extern struct sk_buff *__alloc_skb(unsigned int size,                   |         
                   gfp_t priority, int fclone, int node);               |         
static inline struct sk_buff *alloc_skb(unsigned int size,    <—————             
                    gfp_t priority)                                             
{                                                                               
    return __alloc_skb(size, priority, 0, NUMA_NO_NODE);  -----                      
}                                                              |                 
//最终调用__alloc_skb进行申请一个sk_buff结构体                 |
//他的函数原型在net/core/skbuff.c                              |
/**                                                            |                 
 *  __alloc_skb -   allocate a network buffer     <____________|                             
 *  @size: size to allocate     长度                                                
 *  @gfp_mask: allocation mask  掩码                                             
 *  @fclone: allocate from fclone cache instead of head cache                   
 *      and allocate a cloned (child) skb                                       
 *  @node: numa node to allocate memory on                                      
 *                                                                              
 *  Allocate a new &sk_buff. The returned buffer has no headroom and a          
 *  tail room of size bytes. The object has a reference count of one.           
 *  The return is the buffer. On a failure the return is %NULL.                 
 *                                                                              
 *  Buffers may only be allocated from interrupts using a @gfp_mask of          
 *  %GFP_ATOMIC.                                                                
 */                                                                             
struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,                  
                int fclone, int node)
{
//......
}                                           

原文地址:https://www.cnblogs.com/chenfulin5/p/6073755.html