perrynzhou

专注于系统组件研发

0%

Linux网络内核概述

Linux网络内核概述

Linux网络栈

  • 物理层(L1):提供电信号和一些底层细节
  • 数据链路层(L2):处理端点间的数据传输,最常见的数据链层的标准是以太网,Linux以太网设备驱动运行在这一层
  • 网络层(L3):负责数据包转发和主机编址,Linux内核网络子系统中最常用的是IPv4和IPv6协议
  • 传输层(L4): 完成节点间的数据发送,TCP和UDP是最常见的网络协议
  • 应用层:应用层协议,比如http/https、ftp协议

Linux网络栈的本质

  • Linux内核协议栈任务就是将接收到的数据数据包从数据链路层(L2层,,网络设备驱动)传递给网络层(L3层,IPv4或者IPv6),如果数据包的目的地是当前设备,Linux网络协议栈将数据包传递给传输层(L4层,同时是TCP/UDP);如果数据包需要转发,网络协议栈交给L2层进行传输。对于本设备需要发送的数据包,将从L4依次传递给L2,再有网络驱动程序进行传输,在传输节点可能会发生如下节点:
1
2
3
4
5
6
1.根据协议规则(如IPsec规则或者NAT规则),可能需要对数据包进行修改
2.数据包可能会被丢弃
3.数据包可能导致设备发送错误消息
4.可能会对数据包进行分段
5.可能会对数据包进行重组
6.需要计算数据包的校验和

网络设备

  • 混杂模式:在linux 网络协议栈实现中,有一个net_device表示设备的数据结构,其中有一个promsicuity字段,该字段值大于0,网络协议栈不会丢失那些目的地并非本地主机的数据包,这种模式就是混杂模式,通常该模式用户网络调试。

  • 网络设备中的API:老的网络设备驱动是在中断模式下进行工作的,没接受一个数据包,就需要中断一次,大量实践表明这种中断模式在负载很高的情况下效率非常低下。为了解决这个问题引入了NAPI,当前几乎所有的Linux都支持这种技术,NAPI是在linux kernel 2.5/2.6引入,采用该NAPI,如果负载很高,网络设备驱动将以轮询模式工作,而不是中断模式工作,这就意味着不会再每次接受数据包时候触发中断,而是将数据包放到内核缓存区中,由kernel不断的轮询来取数据包

  • 网络驱动的工作

    • 数据包接受目的地为当前主机,将其传递给网络层(L3),然后传递给传输层(L4)
    • 传输当前主机发送出去的数据包或者转发当前主机接受的数据包
    • 每个数据包不管是接受还是发送出去的,都需要路由子系统执行一次查找,根据查找结果来决定数据包的处理。
    • 每个数据包在经过路由子系统之前会经过netfilter子系统的处理,netfilter子系统在网络栈中5个位置会注册回调函数,数据包经过netfileter的第一个回调函数处理,结果用verdict表示,这个值为NF_DROP,则数据包将会被丢弃;如果为NF_ACCEPT,则数据包继续传输到其他层。netfilter为用户态的iptables提供的基础的架构。
  • 套接字缓冲区

    • linux 网络内核使用sk_buff结构表示一个包含报头的入站(接受路径)或者出站(传输路径)的数据包。
    • 使用skb时候必须遵循skb api,比如需要skb->data指针向前移动,必须通过skb_pull_inline或者skb_pull函数。要冲skb_buff中取回传输层报头,需要调用skb_transport_header函数,同样的,要取回网络层的报头,必须调用skb_network_header函数;取回数据链路层的报头,必须调用skb_mac_header函数,这三个方法都是入参都是skb_buff的结构体。
    • 从物理层接收到数据包后,网络设备驱动会分配一个skb(通过netdev_alloc_skb函数),在数据传输过程中,有时候需要丢失数据包则调用kfree_skb函数。skb_buff中的某些成员是有数据链路层决定的,比如IPv4数据包由14个字节的以太网报头、20到60个字节的IPv4报头、8个字节的UDPv4的报头,最后是数据包的有效载荷。每个skb_buffer的实例都包含了一个net_device的实例,对于接受到来的数据包,这个成员表示接受它的网络设备;对于发送出去的数据包,这个成员则表示发送它的网络设备。