LINUX网络子系统

时间:2021-08-10 11:10:49


 一 内核结构

    按功能,Linux内核可以划分为5个不同的部分,每一部分处理一项明确的功能,同时又向其他内核组件提供这项功能。这种结构也反映到内核的源代码上——这5部分都形成了自己的子树。

    1进程管理

    2内存管理

    3文件系统

    4设备驱动程序

    5网络

    二 网络体系结构

    Linux网络体系结构由以下五个部分组成 1)系统调用接口 2)协议无关几口 3)网络协议 4)设备无关接口 5 设备驱动程序。下面分别简述五个部分:

    1)系统调用接口

    系统调用接口是用户空间的应用程序正常访问内核的唯一合法途径(终端和陷入也可访问内核)。如:

    asmlingkage long sys_getpid(void)

    {

    return current->pid;

    }

    系统调用一般由sys开头 ,前面的修饰符是asmlingkage,表示函数由堆栈获得参数。

    2)协议无关接口

    协议无关接口是由socket来实现的。它提供了一组通用函数来支持各种不同协议。

    通过网络栈进行的通信都需要对 socket 进行操作。Linux 中的 socket 结构是 struct sock ,这个结构是在 linux/include/net/sock.h 中定义的。这个巨大的结构中包含了特定 socket 所需要的所有状态信息,其中包括 socket 所使用的特定协议和在 socket 上可以执行的一些操作。

    网络子系统可以通过一个定义了自己功能的特殊结构来了解可用协议。每个协议都维护了一个名为 proto 的结构(可以在 linux/include/net/sock.h 中找到)。这个结构定义了可以在从 socket 层到传输层中执行特定的 socket 操作

    3)网络协议

    Linux支持多种网络协议,可以在<linux/socket.h>中查到所支持的网络协议:

    #define AF_UNIX        1    /* Unix domain sockets         */

    #define AF_LOCAL    1    /* POSIX name for AF_UNIX    */

    #define AF_INET        2    /* Internet IP Protocol     */

    #define AF_AX25        3    /* Amateur Radio AX.25         */

    #define AF_IPX        4    /* Novell IPX

    … …

    其中每一个所支持的协议对应net_family[]数组中的一项,net_family[]是结构体指针数组,其中的每一项都是一个结构体指针,指向一个net_proto_family 结构

    struct net_proto_family {

    int family;

    int (*create) (struct socket * sock, int protocol);

    short authentication;

    short encryption;

    short encrypt_net;

    struct module *owner;

    };这个结构体中注册了关于协议的信息。

    4)设备无关接口

    设备无关接口是由net_device实现的。任何设备和上层通信都是通过net_device设备无关接口。

    它将协议与具有很多各种不同功能的硬件设备连接在一起。这一层提供了一组通用函数供底层网络设备驱动程序使用,让它们可以对高层协议栈进行操作。

    首先,设备驱动程序可能会通过调用 register_netdevice 或 unregister_netdevice 在内核中进行注册或注销。调用者首先填写 net_device 结构,然后传递这个结构进行注册。内核调用它的 init 函数(如果定义了这种函数),然后执行一组健全性检查,并创建一个 sysfs 条目,然后将新设备添加到设备列表中(内核中的活动设备链表)。在 linux/include/linux/netdevice.h 中可以找到这个 net_device 结构。这些函数都是在 linux/net/core/dev.c 中实现的。

    要从协议层向设备中发送 sk_buff ,就需要使用 dev_queue_xmit 函数。这个函数可以对 sk_buff 进行排队,从而由底层设备驱动程序进行最终传输(使用 sk_buff 中引用的 net_device 或 sk_buff->dev 所定义的网络设备)。dev 结构中包含了一个名为 hard_start_xmit 的方法,其中保存有发起 sk_buff 传输所使用的驱动程序函数。

    报文的接收通常是使用 netif_rx 执行的。当底层设备驱动程序接收一个报文(包含在所分配的 sk_buff 中)时,就会通过调用 netif_rx 将 sk_buff 上传至网络层。然后,这个函数通过 netif_rx_schedule 将 sk_buff 在上层协议队列中进行排队,供以后进行处理。可以在 linux/net/core/dev.c 中找到 dev_queue_xmit 和 netif_rx 函数。

    5)设备驱动程序

    网络栈底部是负责管理物理网络设备的设备驱动程序。例如,包串口使用的 SLIP 驱动程序以及以太网设备使用的以太网驱动程序都是这一层的设备。

    在进行初始化时,设备驱动程序会分配一个 net_device 结构,然后使用必须的程序对其进行初始化。这些程序中有一个是 dev->hard_start_xmit ,它定义了上层应该如何对 sk_buff 排队进行传输。这个程序的参数为 sk_buff 。这个函数的操作取决于底层硬件,但是通常 sk_buff 所描述的报文都会被移动到硬件环或队列中。就像是设备无关层中所描述的一样,对于 NAPI 兼容的网络驱动程序来说,帧的接收使用了 netif_rx 和 netif_receive_skb 接口。NAPI 驱动程序会对底层硬件的能力进行一些限制。

    三 核心数据结构

    网络体系结构中有几个核心数据结构

    1)sk_buf

    网络层的数据都是通过sk_buf来传递的。

    与sk_buf相关的一些数据结构有:socket sock  proto  proto_ops;

    2)net_device

    设备无关层的统一接口。

    与 net_device相关的一些数据结构有device …

    先来看一下sk_buf的数据结构:

    view plaincopy to clipboardprint?

    struct sk_buff

    {

    struct sk_buff        *next,*prev;

    struct sk_buff_head   *list;

    struct sock           *sk;                                  //sock结构指针

    struct timeval        stamp;

    struct net_device     *dev, *rx_dev;

    union /* Transport layer header */

    {

    struct tcphdr   *th;

    struct udphdr   *uh;

    struct icmphdr  *icmph;

    struct igmphdr  *igmph;

    struct iphdr    *ipiph;

    struct spxhdr   *spxh;

    unsigned char   *raw;

    } h;                                                             //传输层头

    union /* Network layer header */

    {

    struct iphdr    *iph;

    struct ipv6hdr  *ipv6h;

    struct arphdr   *arph;

    struct ipxhdr   *ipxh;

    unsigned char   *raw;

    } nh;                                                          //网络层头

    union /* Link layer header */

    {

    struct ethhdr   *ethernet;

    unsigned char   *raw;

    } mac;                                                      //数据链路层头

    struct dst_entry      *dst;

    char                  cb[48];

    unsigned int          len, csum;                   //数据长度,是否被消耗

    volatile char         used;

    unsigned char         is_clone, cloned, pkt_type, ip_summed;   //是否克隆 已克隆 报文类型  校验和

    __u32                 priority;

    atomic_t              users;

    unsigned short        protocol, security;

    unsigned int          truesize;

    unsigned char         *head, *data, *tail, *end;       //data和tail指向当前有效数据的头和尾

    void                  (*destructor)(struct sk_buff *);      //head和end指向socket的头和尾

    …

    };

    struct sk_buff

    {

    struct sk_buff        *next,*prev;

    struct sk_buff_head   *list;

    struct sock           *sk;                                  //sock结构指针

    struct timeval        stamp;

    struct net_device     *dev, *rx_dev;

    union /* Transport layer header */

    {

    struct tcphdr   *th;

    struct udphdr   *uh;

    struct icmphdr  *icmph;

    struct igmphdr  *igmph;

    struct iphdr    *ipiph;

    struct spxhdr   *spxh;

    unsigned char   *raw;

    } h;                                                             //传输层头

    union /* Network layer header */

    {

    struct iphdr    *iph;

    struct ipv6hdr  *ipv6h;

    struct arphdr   *arph;

    struct ipxhdr   *ipxh;

    unsigned char   *raw;

    } nh;                                                          //网络层头

    union /* Link layer header */

    {

    struct ethhdr   *ethernet;

    unsigned char   *raw;

    } mac;                                                      //数据链路层头

    struct dst_entry      *dst;

    char                  cb[48];

    unsigned int          len, csum;                   //数据长度,是否被消耗

    volatile char         used;

    unsigned char         is_clone, cloned, pkt_type, ip_summed;   //是否克隆 已克隆 报文类型  校验和

    __u32                 priority;

    atomic_t              users;

    unsigned short        protocol, security;

    unsigned int          truesize;

    unsigned char         *head, *data, *tail, *end;       //data和tail指向当前有效数据的头和尾

    void                  (*destructor)(struct sk_buff *);      //head和end指向socket的头和尾

    …

    };

    其中pkt_type可以是如下类型之一

    PACKET_HOST specifies packet a sent to the local host.

    PACKET_BROADCAST specifies a broadcast packet.

    PACKET_MULTICAST specifies a multicast packet.

    PACKET_OTHERHOST specifies packets not destined for the local host, but received by special modes (e.g., the promiscuous mode)。

    PACKET_OUTGOING specifies packets leaving the computer.

    PACKET_LOOPBACK specifies packets sent from the local computer to itself.

    PACKET_FASTROUTE specifies packets fast-forwarded between special network cards (fastroute is not covered in this book

    sk_buf的结构图如下图所示:

    下面两张图展示了sk_buff的copy和clone的区别:

    socket构成的队列:

    2) net_device结构

    net_device是设备无关层的接口:这是一个巨型的数据结构

    这个数据结构比较巨大。我们分几组一点一点来看

    1 网络设备的通用字段

    name          网络适配器的名字

    *next          用于连接多个网络设备

    owner         moudle结构指针

    ifindex        除name外的网络设备第二标识符

    iflink           指定了用来发包的网络设备的索引

    state          网络设备的状态可以是以下值:LINK_STATE_START,LINK_STATE_X0FF(替代以前的tbusy字段)

    下列3种方法可以改变state的值:netif_stop_queue(dev);

    netif_start_queue(dev);

    netif_wake_queue(dev);

    trans_start           传输计时开始

    *priv                 指向其私有数据的指针

    qdisc                 映射当前网络服务策略

    refcnt                当前网络设备引用计数

    xmit_lock             发送时的锁

    queue_lock            队列的锁

    xmit_lock_owner   获得发送时的锁的处理器编号

    2 硬件相关字段

    rmem_end      接受内存尾地址

    rmem-start     接受内存首地址

    mem_end        发送内存尾地址

    mem_start      发送内存首地址

    base_addr      网络设备的基地址(见后图)

    irq                  中断号

    if_port            端口号

    3 物理层上的数据

    hard_header_length                     第二层包报头长度

    mtu                                    最大传输单元

    tx_queue_len                           网络设备输出队列最大长度

    type                                   网络适配器硬件类型

    addr_len                               第二层地址长度

    dev_addr[MAX_ADDR_LEN]       第二层地址

    broadcast[MAX_ADDR_LEN]      广播地址

    *mc_list                                    指向具有多播第二层地址的线性表

    mc_count                                  dev_mc_list中的地址数量(多播地址数)

    watchdpg_timeo                       超时时间(从trans_start开始,经过watchdog_timeo时间后超时)

    4 网络层上的数据

    ip_ptr,ip6_ptr,atalk_ptr,dn_ptr,ec_ptr      指向网络适配器第三层协议信息,如ip_ptr指向in_device结构。该结构包含ip地址列表,多播ip列表,ARP协议参数等

    pa_len                                     协议地址长度

    pa_dstaddr                                 点对点连接中的目的地址

    flags                                      各种不同的开关,他们都可以通过ifconfig命令来设置

    Flag

    Meaning

    IFF_UP

    The network device is activated and can send and receive packets.

    IFF_BROADCAST

    The device is broadcast-enabled, and the broadcast address pa_braddr is valid.

    IFF_DEBUG

    This flag switches the debug mode on (currently not used by any driver)。

    IFF_LOOPBACK

    This flag shows that this is a loopback network device.

    IFF_POINTOPOINT

    This is a point-to-point connection. If this switch is set, then pa_dstaddr should contain the partner's address.

    IFF_NOARP

    This device does not support the Address Resolution Protocol (ARP) (e.g., in point-to-point connections)。

    IFF_PROMISC

    This flag switches the promiscuous mode on. This means that all packets currently received in the network adapter are forwarded to the upper layers, including those not intended for this computer. This mode is of interest for tcpdump only

    IFF_MULTICAST

    This flag activates the receipt of multicast packets. ether_setup() activates this switch. A card that does not support multicast should delete this flag.

    IFF_ALLMULTI

    All multicast packets should be received. This is required when the computer is to work as multicast router. IFF_MULTICAST has to be set in addition.

    IFF_PORTSEL

    Setting of the output port is supported by the hardware.

    IFF_AUTOMEDIA

    Automatic selection of the output medium (autosensing ) is enabled.

    IFF_DYNAMIC

    Dynamic change of the network device's address is enabled (e.g., for dialup connections)。

    5 设备驱动程序的函数

    init()                 搜索并初始化网络设备

    uninit()             注销网络设备

    destructor()     当网络设备的最后一个引用refcnt被删除时调用此函数

    open ()            打开网络设备

    stop()               关闭网络设备

    hard_start_xmit()        发送包,成功返回0,否则返回1

    get_stats()                  获取网络设备状态信息,这些信息以net_device_stats 结构的形式返回

    get_wireless_stats()   获取无限网络设备的状态信息,这些信息以iw_statistics 结构的形式返回

    set_multicast_list()     将多播MAC地址传给网络适配器

    watchdog_timeo()      超时处理函数

    do_ioctl()                    向网络驱动程序传递网络适配器相关的ioctl()命令

    set_config()                运行时改变网络适配器的配置

    上面所列的方法依赖于所使用的网络适配器,也就是说如果需要他们的功能则必须由驱动程序来提供。

    下面所列的方法较少依赖于适配器,不必由驱动程序相关方法实现。

    hard_header()                        根据源和目标第二层地址创建二层报头

    rebuild_header()                     重建第二层报头

    hard_header_cache()             用硬报头缓存中保存的数据填充第二层报头

    header_cache_update()         更改硬报头缓存中保存的第二层报头数据

    hard_header_parse()            从套接字缓冲区的包数据空间读取第二层报头的发送地址

    set_mac_address()                设mac地址

    change_mtu()                        改变mtu长度