Linux内核网络

讨论Linux内核网络栈地实现及其原理,深入而详尽地分析网络子系统及其架构。讲解数据包在Linux内核网络栈中的传输过程,阐述其与网络各层及各子系统之间的交互,探讨各种网络协议的实现方法。

1. Linux网络栈

  • 1.1 网络设备

    网络设备驱动程序位于数据链路层,表示网络设备的net_device结构体如下

    • 设备的IRQ号
    • 设备的MTU
    • 设备的MAC地址
    • 设备的名称, 如eth0或eth1
    • 设备的标志,如状态为up还是down
    • 与设备相关联的组播地址清单
    • promiscuity计数器
    • 设备支持的功能,如GSO或GRO
    • 网络设备回调函数的对象,这个对象由函数指针组成,如用于打开和停止设备、开始传输、修改网络设备MTU等的函数
    • ethtool回调函数对象,它支持通过运行命令行实用程序ethtool来获取有关设备的信息
    • 发送队列和接收队列数(如果设备支持多个队列)
    • 设备最后一次发送数据包的时间戳

    如果计数器promiscuity的值大于0,网络栈就不会丢弃那些目的地并非本地主机的数据包。这样,tcpdump和wireshark等数据包分析程序(嗅探器)就能对其加以利用。嗅探器会在用户空间打开原始套接字,从而捕获此类发往别处的数据包。为了能够同时运行多个嗅探器,特将promiscuty声明成了计数器而非布尔变量。每运行一个嗅探器,计数器promicuity的值就加1;每关闭一个嗅探器,该值减1。如果这个计数器的值为0,就说明没有运行任何嗅探器,因此设备退出混杂模式.

    老式设备驱动程序是在中断驱动模式下工作的。这意味着每接收一个数据包,就需要中断一次。新软件技术NAPI(New API), 如果负载很高时,网络设备驱动程序将在轮询模式,而不是中断驱动模式下运行,驱动程序会将数据包存储在缓冲区,由内核不时地向驱动程序轮询。从内核3.11起,Linux新增了频繁轮询套接字(Busy Polling Sockets)的功能,用于那些不惜以提高CPU使用率为代价而尽可能降低延迟的套接字应用程序

  • 1.2 数据包的收发

    网络设备驱动程序的主要任务是:接收目的地为当前主机的数据包,并将其传递给网络层(L3);传输当前主机生成的外出数据包或转发当前主机收到的数据包

    对于每个数据包,无论是它接收到的还是发送出去的,都需要在路由子系统执行一次查找操作。根据路由子系统的查找结果,决定是否应对数据包进行转发以及该从哪个接口发送出去。决定数据包在网络栈中传输过程的因素并非只有路由子系统查找结果。在网络栈中有5个位置,Netfilter子系统在其中注册了回调函数。这些回调函数通常被称为Netfilter钩子。Linux内核中的Netfilter子系统为著名的用户空间包iptables提供了基础架构

    除Netfilter钩子外,IPsec子系统也可能影响数据包的旅程。IPsec提供了一种网络层安全解决方案,它使用ESP和AH协议。IPsec在IPv6是强制执行的,而在IPv4中是可选的。IPsec有两种运行模式:传输模式和隧道模式。很多VPN(Virtual Private Network)解决方案都以IPsec为基础

    套接字缓冲区SKB。从线路上收到数据包后,网络设备驱动程序会分配一个SKB,SKB包含数据包的报头(L2,L3和L4报头)和有效载荷。在数据包沿网络栈传输的过程中,可能添加或删除报头

    每个第2层网络接口都由一个L2地址标识。就以太网而言,其为一个长48位的MAC地址,由制造商分配

2. IPv4

IPv4协议可在任何两台主机之间提供端到端的连接。IP层的另一项功能是转发数据包(也叫路由选择)以及管理路由选择信息表

  • 2.1 Netlink

    Netlink套接字旨在提供一种更灵活的用户空间进程与内核间通信方法,用于替代笨拙的IOCTL.

    IOCTL处理程序不能从内核向用户空间发送异步消息,而Netlink套接字则可以。要使用IOCTL,还存在另一个麻烦,必须定义IOCTL号,Netlink的运行模型只需使用套接字API打开并注册一个Netlink套接字,它就会处理与内核Netlink套接字的双向通信

    用于处理TCP/IP用户空间包:net-tools和iproute2。iproute2包含ip, tc, ss, lnstat, bridge。net-tool包含ifconfig, arp, route, netstat, hostname, rarp

    ip route add 192.168.2.11 via 192.168.2.20
    ip route del 192.168.2.11 
    ip monitor route
    

    这个命令通过rtnetlink套接字,从用户空间发送一条添加路由选择条目的Netlink消息(RTM_NEWROUTE)。这条消息由rtnetlink内核套接字接收,并由方法rtnetlink_rcv()处理。最终,添加路由选择条目是通过inet_rtm_newroute()完成的,由方法fib_table_insert()完成插入转发信息库(FIB,即路由选择数据库)的工作,并通知所有注册了RTM_NEWROUTE消息的侦听者

  • 2.2 IPv4报文

    id分段标识,对SKB进行分段时,所有分段的id值都必须相同,对于分段后的数据包,则要根据各个分段的id对其进行重组

    frag_off(分段偏移量,16位),后13位指出了分段的偏移量,前三位,001表示后面还有其它分段,010表示不分段, 100表示拥塞

  • 2.3 IPv4路由转发

    IPv4路由选择子系统及其使用的主要数据结构,例如路由选择表、转发信息库和FIB别名、FIB TRIE等。

    两个以太网LAN1和LAN2,其中LAN1包含子网192.168.1.0/24,而LAN2包含子网192.168.2.0/24,在这两个LAN之间,有一台转发路由器,它有两个以太网接口卡,其中连接到LAN1的网络接口为eth0,其IP地址为192.168.1.200,而连接到LAN2的网络接口为eth1,其IP地址为192.168.2.200。出于简化考虑,假设转发路由器没有运行防火墙守护程序,从LAN1向LAN2发送流量,对到来的数据包进行转发的过程被成为路由选择,是根据被称为路由选择表的数据结构进行的

    无类域间路由选择(Classless Inter-Domain Routing, CIDR)用0.0.0.0/0表示路由

    #添加默认网关
    ip route add default via 192.168.2.1
    

    对于小型网络,管理FIB的工作可以由系统管理员手工完成,因为这种类型的网络拓扑几乎是静态的。而对于核心路由器来说,拓扑是静态的,管理FIB的工作通常由用户空间路由选择守护程序负责,这些用户空间守护程序通常用于维护独立的路由选择表,偶尔还会与内核路由选择表进行交互。

    在3.6版之前的内核中,无论在接收还是传输路径中,查找都包含两个阶段:首先在路由选择缓存中查找,如果没有找到再在路由选择表中查找。查找工作方法fib_lookup(),其首先在本地FIB表中搜索,再在主FIB表查找

    路由选择子系统的主数据结构是路由选择表,由结构fib_table表示。简单地说,路由选择表的每个条目都指定了前往特定子网的流量所对应的下一跳。每个路由选择条目都包含一个fib_info对象,其中存储了路由选择条目参数(出站网络设备、优先级、路由选择协议标识符)。fib_info对象包含:

    • fib_net: fib_info对象所属的网络命名空间
    • fib_protocol: 路由选择协议标识符。从用户空间添加路由选择规则时,如果没有指定路由选择协议ID,fib_protocol将被设置为RTPROT_BOOT。管理员添加路由时,可能会使用修饰符proto static,指出路由是由管理员添加的。ip route add proto static 192.168.5.3 via 192.168.2.1
    • fib_scope: 目标地址范围,它为地址和路由都指定了范围。简单地说,指出了主机相对于其它节点的距离
      • 主机(RT_SCOPE_HOST): 节点无法与其它网络节点通信。环回地址的范围就是主机
      • 全局(RT_SCOPE_UNIVERSE): 地址可用于任何地方
      • 链路(RT_SCOPE_LINK): 地址只能从直连主机访问
      • 场点(RT_SCOPE_SITE): 仅用于IPv6
      • 找不到(RT_SCOPE_NOWHERE): 目的地不存在
      • fib_priority: 路由的优先级,默认为0,表示最高优先级。ip route add 192.168.1.10 via 192.168.2.1 metric 5, ip route add 192.168.1.10 via 192.168.2.1 priority 5, ip route add 192.168.1.10 via 192.168.2.1 preference 5。命令ip route参数的metric与fib_info对象字段fib_metrics没有任何关系
      • fib_dev: 将数据包传输到下一跳的网络设备

    策略路由选择。使用策略路由选择时,仅根据目标地址为数据包选择路由,而不考虑其它因素(如源地址和TOS)。系统管理员可添加多达255个路由选择表。不适用策略路由选择时,将创建两个路由选择表:本地表和主表。主表的ID为254(RT_TABLE_MAIN),本地表的ID为255(RT_TABLE_LOCAL)。只有内核才能在本地表添加路由选择条目

    #应用这条规则后,所有从192.168.2.103发送到192.168.1.17的数据包都将被禁止通过
    ip route add prohibit 192.168.1.17 from 192.168.2.103,
    

    ip route show table local显示本地表路由规则, 默认显示主表

    FIB别名。有些情况下,会针对同一个目标地址或子网创建多个路由选择条目。这些路由选择条目的唯一差别是其TOS不同,在这种情况下,将为每条路由创建一个fib_alias对象,而不是fib_info对象。fib_alias相对来说消耗资源更少

    ip route add 192.168.1.10 via 192.168.2.1 tos 0x2
    ip route add 192.168.1.10 via 192.168.2.1 tos 0x4
    

    rout -n输出中标志,

    • U(路由处于up状态)
    • H(目标为主机)
    • G(使用网关)
    • R(为动态路由选择恢复路由)
    • D(由守护程序或重定向消息动态加入)
    • M(被路由选择守护程序或重定向消息修改过)
    • A(由addrconf加入)
    • !(阻塞路由)
    #查看路由表
    cat /etc/iproute2/rt_tables
    
  • 2.4 策略路由

    #所有IPv4 TOS字段为0x04的数据包都将根据表252中的路由选择规则进行处理
    ip rule add tos 0x04 table 252
    
    ip route add default via 192.168.2.10 table 252
    
    ip rule show
    

3. Linux邻接子系统

邻接子系统负责发现当前链路上的节点,并将L3地址转换为L2地址。在IPv4中,实现这种转换协议为地址解析协议,而在IPv6中则为邻居发现协议(NDISC)

  • 3.1 用户空间与邻接子系统之间的交互

    ip neigh show
    

4. IPv6

IPv6增加的新功能,拓展报头、组播侦听者发现(MLD)协议和自动配置过程等

  • 4.1 IPv6报头

  • 4.2 接收IPv6数据包

5. Netfilter

  • 5.1 Netfilter

    Netfilter子系统提供了如下功能:数据包选择(iptables)、数据包过滤、网络地址转换(NAT)、数据包操纵(在路由选择之前或之后修改数据包报头的内容)、连接跟踪、网络统计信息收集

    在网络栈中有5个地方设置了Netfilter挂载点

    • NF_INET_PRE_ROUTING: 挂载点位于ip_rcv()/ipv6_rcv(),它处于路由选择子系统查找之前
    • NF_INET_LOCAL_IN: 挂载点位于ip_local_deliver()/ip6_input()中,对于所有发送给当前主机的入站数据包,经过挂载点NF_INET_PRE_ROUTING并执行路由选择子系统查找后,都将到达这个挂载点
    • NF_INET_FORWARD: ip_forward()/ip6_forward()
    • NF_INET_POST_ROUTING: ip_output()/ip6_finish_output2()中
    • NF_INET_LOCAL_OUT: __ip_local_out()/__ip6_local_out()中,当前主机生成的所有出站数据包都在经过这个挂载点后到达挂载点NF_INET_POST_ROUTING

    连接跟踪能够让内核跟踪会话,主要目标是为NAT打下基础。如果没有设置CONFIG_NF_CONNTRACT_IPV4,就不能构建IPv4-NAT模块,如果没有CONFIG_NF_CONNTRACT_IPV6,就不能构建IPv6-NAT模块

  • 5.2 iptables

    #这条规则的意思是,将目标端口为5001的UDP入站数据包转存到系统日志中
    iptables -A INPUT -p udp --dport=5001 -j LOG --log-level 1
    
  • 5.3 NAT

    网络地址转换(NAT)模块主要用于处理IP地址转换或端口操纵。NAT最常见的用途之一是,让局域网一组使用私有IP地址的主机能够通过内部网关访问internet

6. IPsec

IPsec是一组协议,它们对通信会话中的每个数据包进行身份验证和加密,以确保IP流量的安全。大部分安全服务都是由两个主要的IPsec协议提供的:AH协议和ESP协议。另外,IPsec还可以防止窃听以及数据包的重新发送(重放攻击)

IPsec由多种运行模式,其中最重要的是传输模式和隧道模式。在传输模式下,对IP数据包的有效负载进行加密;而在隧道模式下,对整个IP数据包进行加密,并使用新的IP报头将其封装到新的IP数据包中。使用基于IPsec的VPN时,通常采用隧道模式。

图描述的是IPv4 ESP数据包。对于IPv4 AH数据包,将调用方法ah_input()而不是esp_input()。同理,对于IPv4 IPCOMP数据包,将调用方法ipcomp_input()而不是esp_input()

Resources