📖Linux|网络
2024-6-2|2024-11-23
黎明
type
status
date
slug
summary
tags
category
icon
password
进度
专业术语网络拓扑结构OSI七层模型与TCP/IP模型物理层数据链路层网络层传输层TCPUDP应用层HTTPSHTTP2eBPF/XDP背景挂载点(AF_XDP)工作流eBFP/XDP DDoSeBPF/XDP ForwardSDNIPSecBGPUCX
专业术语
- 总线型拓扑 Bus Topology
- 环型拓扑 Ring Topology
- 星型拓扑 Star Topology)
- 网状型拓扑 Mesh Topology
- 树型拓扑 Tree Topology
- 注册的插座 RJ Registered Jack
- 网络接口卡 NIC Network Interface Card
- 集线器 HUB
- 交换机 Switch
- 路由器 Router
- 开放系统互联模型 Open Systems Interconnection Model
- 物理层 Physical Layer
- 数据链路层 Data Link Layer
- 网络层 Network Layer
- 传输层 Transport Layer
- 会话层 Session Layer
- 表示层 Presentation Layer
- 应用层 Application Layer
- 封装 Encapsulation
- 解封装 Decapsulation
- 吞吐量 Throughput
- 带宽 Bandwidth
- 延迟 Latency
- 提出 Raise
- 中断请求 Interrupt ReQuest
- 接收端缩放 Receive Side Scaling(RSS)
- 接收数据包转向 Receive Packet Steering (RPS)
- 接收数据流转向 Receive Flow Steering (RFS)
- 亲和力 Affinity
- 处理器间中断 Inter-processor Interrupt(IPI)
- 路径MTU发现 Path MTU Discovery PMTUD
网络拓扑结构
RJ45
NIC
HUB
Switch
Router
OSI七层模型与TCP/IP模型
OSI七层模型和TCP/IP模型都是为了解决网络通信问题而设计的
OSI(概念模型) | TCP/IP(实践模型) |
应用层 | 应用层 |
表示层 | ㅤ |
会话层 | ㅤ |
传输层 | 传输层 |
网络层 | 网络层 |
数据链路层 | 数据链路层 |
物理层 | 物理层 |
从模型定义中可以解读两个事实:
- 数据需要封装和解封装
- 若读取某层数据,则需解封装下层协议
物理层
传输对象 | 比特流 | ㅤ |
带宽 | 单位时间内传输的数据量 | 网卡:Mbps/Gbps,运营商:Money |
吞吐量 | 单位时间内成功传输的数据量 | 影响因素:带宽、延迟、网络拥塞、丢包率、协议开销、硬件性能 |
🤔Linux 网卡收发数据?参考
- 网卡需要网卡驱动才能使用
- 推荐使用设备制造商提供的官方驱动程序,保证兼容性和稳定性。
- 接收报文
- 数据包进入物理网卡,目的地址不属于该网卡则被丢弃。
- 网卡通过DMA将数据写入内存地址;内存地址和初始化由驱动分配。
- 网卡向CPU发送中断请求,通知数据已到达。
- CPU根据中断向量表调用中断处理函数,该函数会调用驱动中对应函数。
- 驱动首先禁用中断功能,标识驱动已知数据在内存中,并告知网卡收到下个数据包直接写入内存,无需再次中断通知CPU,无需频繁中断CPU而降低效率。
- 启动软中断。硬中断处理函数执行间不可被中断,若执行时间太长则影响CPU无法响应其他硬件中断。
🤔Linux 网卡收发数据?多核CPU?
- NIC收到数据报文产生的IRQ只能被一个CPU处理,从而只有一个CPU会执行软中断处理函数;即一个RingBuffer上同一个时刻只有一个CPU在读取数据。
RingBuffer空间是有限的,当收包速率大于单核CPU处理速率时,RingBuffer可能被占满,占满之后新数据包会自动被丢弃。
对于多核系统,同时只有一个CPU处理RingBuffer数据包效率较低。
- 硬件解决方案:网卡内部支持多RingBuffer,NIC收到数据通过Hash到某RingBuffer。触发的IRQ可通过系统或手动配置IRQ Affinity指定CPU。
- 软件解决方案:Receive Packet Steering和Receive Flow Steering配合使用;NIC收到数据DMA将数据存入还是一个RingBuffer,NIC触发IRQ通知到一个CPU,该CPU通过驱动poll将数据读取。RPS是在单个CPU 将数据从Ring Buffer读取后生效,为每个Packet 计算Hash并发到对应CPU的backlog中,并通过 Inter-processor Interrupt(IPI) 告知目标 CPU 来处理 backlog。后续 Packet 的处理流程就由这个目标 CPU 来完成。从而实现将负载分到多个 CPU 的目的。
🤔Linux 物理网卡 vs 虚拟网卡?
特点 | 物理网卡设备 | 虚拟网卡设别 |
实现方式 | 以太网卡,Wi-Fi等物理硬件 | 操作系统或网络软件创建和管理 |
设备名称 | eth0 wlan0 | veth0 tun0 br0 virbr0 |
用途 | 直接网络,收发流量 | 虚拟化,VPN,VLAN |
代表 | NIC | KVM Docker LXC Kubernetes |
性能 | 硬件带宽和能力 | 系统处理器、内存资源 |
网络设备 | /sys/class/net
> ip link show | 特殊情况:Tun/Tap 设备
/dev/net/tun /dev/net/tap |
🤔Linux 网络设备多IP?
在 Linux 中,物理网卡和虚拟网卡都可以配置多个 IP 地址。无论是物理设备(如
eth0
、wlan0
)还是虚拟设备(如 br0
、veth0
、tun0
)。数据链路层
- 帧的封装与解封装
- 物理寻址
协议 | |
以太网协议 | Ethernet_II |
无线局域网协议 | ㅤ |
VLAN | ㅤ |
PPP | ㅤ |
PPPoE | ㅤ |
以太网协议结构(Ethernet Frame)
实际传输帧数据多8个字节,前导码和SFD。它们的作用非常关键。
前导码:在数据传输过程中,发送端和接收端的时钟可能存在微小差异。前导码通过交替的1和0信号,使接收端的适配器能够调整并同步其时钟,以便准确接收后续的比特流。(位同步)
标识帧的开始:帧开始定界符的最后两位告诉接收端,帧的正式数据即将到来,准备好接收。
- 前导码:7字节,用于同步发送方和接收方的时钟信号。
- 起始帧定界符(SFD):1字节,表示帧的开始。
- 目标MAC地址:6字节,指示帧的目标设备。
- 源MAC地址:6字节,指示帧的发送设备。
- 类型/长度:2字节,表示帧中数据部分的协议类型或长度(如IP、ARP等)。值小于0x0600表示长度,否则表示类型。
- 数据:46至1500字节,承载实际传输的数据,最小46字节,最大1500字节。
- 帧校验序列(FCS):4字节,用于错误检测(通常使用循环冗余校验CRC)。
EtherType是以太网帧头中重要字段,标识该帧所承载的上层协议类型
EtherType | 协议 | 描述 |
0x0800 | IP | IPv4 |
0x0806 | ARP | ㅤ |
0x8100 | IEEE 802.1Q VLAN | 虚拟局域网 |
0x86DD | IPv6 | ㅤ |
接收报文:
- 操作系统中专门处理软中断进程——ksoftirqd。当ksoftirqd接收到软中断时,它会调用相应的软中断处理函数。第6步中由网卡驱动模块触发的软中断,ksoftirqd会调用网络模块中的net_rx_action函数。
- net_rx_action函数会调用网卡驱动中的poll函数,逐个处理数据包。
- 在poll函数中,驱动程序会逐个读取网卡写入内存的数据包,该数据包的格式只有驱动程序知道。
- 驱动程序将内存中的数据包转换为内核网络模块可识别的skb格式,并调用napi_gro_receive函数。
- napi_gro_receive函数会处理与gro相关内容,包括合并可合并的数据包,然后检查RPS是否启用,若启用则调用enqueue_to_backlog函数。
- 在enqueue_to_backlog函数中,将数据包放入CPU的softnet_data结构体的input_pkt_queue队列中,然后返回。如果队列已满,则会丢弃该数据包。
- CPU在自身的软中断上下文中处理input_pkt_queue队列中的网络数据(调用__netif_receive_skb_core函数)。
- 若未启用RPS,napi_gro_receive函数会直接调用__netif_receive_skb_core函数。
- 检查是否存在AF_PACKET类型的套接字,若存在则将数据包复制给该套接字。(tcpdump)
- 调用相应的协议栈函数,将数据包交给协议栈处理。
- 在内存中所有数据包处理完成后(即poll函数执行完成),启用网卡的硬中断,这样当网卡接收到下一批数据时,将会通知CPU。
网络层
负责主机间的路由选择和数据包传输。在网络层,数据封装成数据报,并通过路由器在不同网络之间传输。
- 路由选择 (Routing): 静态路由和动态路由(BGP)。目标是选择最优路径将数据包从源地址发送到目的地址。
- 逻辑寻址:IPv4 IPv6
- 分组转发:将数据包封装成数据包
IPv4 | IPv6 | |
协议头长度 | 20B~60B | 40B |
Ver | 0100 | 0110 |
IHL | 15*4=60 | ㅤ |
TL & PL | 2^16=64KB | 2^16=64KB |
表示方式 | 4组8位 | 8组16位 |
TTL & Hop | 8=63 | ㅤ |
TOS & TC | 服务类型 | 优先级 |
ㅤ | ㅤ | ㅤ |
🤔IPv4 vs IPv6 分片报文?
IPv4: Identification F Fragment offset 3个字段标识;
分片报共享Identification分片报大小是8的整数倍
- Identification: 每个数据报在生命周期内都是唯一的,通常是通过一个递增的计数器来生成的。当主机发送一个新的IP数据报时,Identification 字段的值会从某个起始值(通常是0)开始,之后每发送一个数据报,这个字段的值会增加1。(16bit)
- Frag: 3bit-[0][0|1][0|1]; 第1位保留0;第2位=1(DF Do not fragment);表示路由器不能对报文进行分片处理;第3位=1(MF,More fragment)多分片,除了最后一个分片的MF位设置为0外,其他所有分片的MF位均设置1,以便接收者直到收到MF位为0的分片为止。
- Fragmentation offset(分片偏移):标识某个分片在分组中的位置.
eg: IPv4 发送4000B数据,MTU=1500,IPv4 IHL=20B,IPv4 PlayLoad = 3980B
分片1
Identification:12345
Flags: 001
FragmentOffset = 0
Data Size: 20B+1480B
分片2
Identification:12345
Flags: 001
FragmentOffset = 185
Data Size: 20B+1480B
分片3
Identification:12345
Flags: 000
FragmentOffset = 370
Data Size: 20B+1020B
💡 TCP/UDP 分片报文,只有第1个分片存在TCP/UDP协议头。
eg: IPv6源端 发送4000B数据,MTU=1500,IPv6 IHL=40B,IPv4 PlayLoad = 3960B
分片扩展头部
示例:
分片1
报文长度 = 1496
负载长度 = 1456
数据长度 = 1448
偏移 = 0
M = 1
标识符 = 2439898
分片2
报文长度 = 1496
负载长度 = 1456
数据长度 = 1448
偏移 = 181
M = 1
标识符 = 2439898
分片3
报文长度 = 1112
负载长度 = 1072
数据长度 = 1064
偏移 = 362
M = 0
标识符 = 2439898
💡分片报文的重组和分片都是耗CPU的,特别在高速转发网络;默认1500B
IPv4分片位置
- 发送端:若MTU小于发送数据报大小,可以主动进行分片。
- 中间路由器:若传输路径中遇到MTU更小的网络,且数据报DF未设置,路由器将数据报进行分片。若DF设置1,路由器丢弃该数据报并向发送方发送ICMP消息
IPv4重组位置
- 目标主机重组
- 中间路由器不重组
IPv6分片位置
- 发送端分片:若MTU小于发送数据报大小,可以主动进行分片。强制要求PMTUD
- 中间路由器:不允许分片。收到超链路MTU数据报时发送ICMPv6。
Packet Too Big
IPv6重组位置
- 目标主机重组
- 中间路由器不重组
🤔IP数据报丢失如何重发以及丢失的原因?
丢失原因 | 原因 | 现象 |
网络拥塞 | 在网络中路由器或交换机过载无法处理传入数据报,且缓存区已满,则丢包 | 网络过载,特别是高峰时段 |
MTU不匹配导致的丢弃 | 数据报大小超过MTU且DF=1,中间路由器则丢包且返回ICMP | 过大数据报导致中途链路设备丢弃 |
路由器或设备故障 | 设备故障 | ㅤ |
TTL过期 | 路由器会丢弃数据报并返回一个ICMP "Time Exceeded" 消息给发送方 | 当数据报循环或被传输的跳数过多导致TTL过期 |
防火墙和安全策略 | 命中安全策略而被丢弃 | ㅤ |
数据包损坏 | 由于物理链路干扰、硬件故障或传输错误,数据包可能会被损坏 | ㅤ |
IP协议本身是无连接、不可靠的。某些情况下,可通过ICMP消息反馈。
ICMP消息 | 含义 |
ICMP Destination Unreachable | 目的不可达 |
ICMP Time Exceeded | 超时 |
ICMPv6 Packet Too Big | 报文太大 |
__netif_receive_skb_core
会调用list_for_each_entry_rcu
函数;
deliver_skb
调用协议层注册的处理函数; arp_rcv() ip_rcv() ipv6_rcv()
- 检查无效报文
- 进入netfilter网络模块,PREROUTING钩子函数
20-21.routing进行路由,如果目的IP不是本地IP,若未开启ip_forward功能,则丢报。若开启ip_forward功能,则调用ip_forward函数。
- 进入netfilter网络模块,FORWARD 钩子函数
网络层主要对各种协议解析和处理
- 入口函数规则:protocolname_rcv
arp_rcv ip_rcv ipv6_rcv
- 结束函数规则:protocolname_rcv_finish
ip_rcv_finish arp_rcv_finish
支持路由,NAT,防火墙操作
- netfilter不对数据报复制
- netfilter实现中使用ip_conntrack结构记录连接状态。是一个协议无关状态。ip层协议维护ip_conntrack哈希表
iptables(netfileter) 四表五链
表\链 | PREROUTING | INPUT | FORWARD | OUTPUT | POSTROUTING |
RAW | ✅ | 🙅 | 🙅 | ✅ | 🙅 |
MANGLE | ✅ | ✅ | ✅ | ✅ | ✅ |
NAT | ✅ | 🙅 | 🙅 | ✅ | ✅ |
FILTER | 🙅 | ✅ | ✅ | ✅ | 🙅 |
conntrack 通俗来讲是数据报路径上的一块记忆存储。
Linux为每个经过网络堆栈的数据包,生成一个新的连接记录项,跟踪并记录连接的状态。
结合代码 | 含义 |
dst.protonum | 协议类型 tcp/udp/icmp/dccp/sctp/gre |
src.u3.ip | 源地址 |
dst.u3.ip | 目的地址 |
src.u.udp.port | 源端口 |
dst.u.udp.port | 目的端口 |
状态选项(状态表) | |
INVALID | 表示分组对应的连接是未知的; |
ESTABLISHED | 表示分组对应的连接已经进行了双向分组传输,也就是说连接已经建立; |
NEW | 表示这个分组需要发起一个连接,或者说,分组对应的连接在两个方向上都没有进行过分组传输; |
RELATED | 表示分组要发起一个新的连接,但这个连接和一个现有的连接有关;例如:FTP 的数据传输连接和控制连接之间就是 RELATED 关系。 |
传输层
TCP
是一种面向连接的、可靠的、基于字节流的传输通信协议;数据在TCP层称为流(Stream),数据分组称为分段(Segment)。
- Data Offset: 4bit = 1111 表示TCP头部长度,也是数据的偏移位置。注意长度是4的倍数;(15*4=60)表示TCP头部最大60字节。
- Checksum:发送者将TCP报文段的头部和数据部分的和计算出来,再对其求反码。
- 建立连接
connection establishment
- 数据传输
data transfer
- 连接终止
connection termination
Linux将TCP连接抽象为套接字(Socket)表示本地端点,给程序使用。在连接的生命周期内,要经历一系列的状态改变。
- 服务器端执行listen函数后建立两个队列
- SYN队列:存放完成二次握手结果
- ACCEPT队列:存放完成三次握手结果
backlog参数定义
- 三次握手建立连接,连接建立过程中初始化很多参数,比如序列号
- 客户端向服务端发送一个SYN,该报携带客户端为这个连接而设定的随机数A作为消息序列号。
- 服务器端收到一个合法的SYN包后,把该包放入SYN队列中;回送一个SYN/ACK。ACK的确认码应为A+1,SYN/ACK包本身携带一个随机产生的序号B。
- 客户端收到SYN/ACK包后,发送一个ACK报,该包的序号被设定为A+1,而ACK的确认码则为B+1。然后客户端的connect函数成功返回。当服务器端收到这个ACK包的时候,把请求从SYN队列中移出,放至ACCEPT队列中。
🤔为什么需要3次?2次行不行?
如果服务器端接到了客户端发的SYN后回了SYN-ACK后客户端掉线了,服务器端没有收到客户端回来的ACK,那么,这个连接处于一个中间状态,既没成功,也没失败。于是,服务器端如果在一定时间内没有收到的TCP会重发SYN-ACK。在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s才知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会断开这个连接。
维护中间状态消耗资源;tcp_synack_retries减少重试次数;tcp_max_syn_backlog增大SYN连接数;tcp_abort_on_overflow决定超出能力时的行为;
“三次握手”的目的是“为了防止已失效的连接(connect)请求报文段传送到了服务端,因而产生错误”,也即为了解决“网络中存在延迟的重复分组”问题。例如:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。
可靠性 | 健壮性 |
序号:
报文段排序和检查重复数据 | 校验:
检查报文传输,无错传输 |
确认和计时器
检测和纠正丢包或延迟 | 流控制 |
ㅤ | 拥塞控制 |
ㅤ | ㅤ |
TCP报文发送者称自己的字节流的编号为序号,称接收到对方的字节流编号为确认号
- 第1个报文序列号=1,应该是相对序号。实际值应该是(随机值+1)。随机值在建立连接过程中确定。
🤔第1个报文中确认报文 数据:0 bytes是一定的吗?存在大于0的情况?存在大于1460的情况?
- 第2、3、4、5报文并非接收一个就发送一个,而是在接收到一定数量的连续字节流后才发送确认
- 第5个报文,接收字节流存在不连续时,确认号从是缺失的序号(可靠的字节流传输)。
流量控制用来避免主机分组发送得过快而使接收方来不及完全收下,一般由接收方通告给发送方进行调控。
- TCP使用滑动窗口协议实现流量控制。接收方在“接收窗口”域指出还可接收的字节数量。发送方在没有新的确认包的情况下至多发送“接收窗口”允许的字节数量。接收方可以修改窗口值。
- 当窗口值等于0,发送方停止进一步发送数据,发送方无法发出数据直至收到接收方修改窗口的指示。
- 若TCP的数据包中发送很少字节,相对于TCP包头是很大的开销。即吞吐量低。解决这个问题,就要避免对小的window size做出响应,直到有足够大的window size再响应。
拥塞控制是发送方根据网络的承载情况控制分组的发送量,以获取高性能又能避免拥塞崩溃(下降几个数量级)
最大分段大小 (MSS)是在单个分段中TCP愿意接受的数据的字节数最大值。MSS应当足够小以避免IP分片,它会导致丢包或过多的重传。在TCP连接建立时,双端在SYN报文中用MSS选项宣布各自的MSS,这是从双端各自直接相连的数据链路层的最大传输单元(MTU)的尺寸减去固定的IP首部和TCP首部长度。以太网MTU为1500字节, MSS值可达1460字节。使用IEEE 802.3的MTU为1492字节,MSS可达1452字节。如果目的IP地址为“非本地的”,MSS通常的默认值为536(这个默认值允许20字节的IP首部和20字节的TCP首部以适合576字节IP数据报)。
🤔TCP的粘包、拆包问题?
从TCP协议定义中可知,协议中并未定义数据长度字段,也就是说接收端不知道分界在哪里?
粘包、拆包原因是TCP发送发送缓存区的原因:
若缓存区空闲比较多,可以存放多个业务数据包,那么就一起发送。(粘包现象)
若缓存区只能存储半个数据包,那么就需要分批发送。(拆包现象)
比如HTTP, FTP主流应用协议已经解决了粘包、拆包问题。通过设置分隔符、设置数据头+数据体方式。总结2点:
- 设置分隔符
- 固定消息头+变长消息体(固定消息头存储长度信息)(比较适合自定义协议)
- 固定消息长度(不推荐)
终止连接
- 连接终止使用了四路握手过程,每一侧都独立地被终止。
- 当一个端点要停止它这一侧的连接,就向对侧发送FIN,对侧回复ACK表示确认。
- 主动发起关闭即可以是客户端也可以是服务端,一般都是客户端主动发起
🤔TCP半开状态?
即一侧关闭了连接,不再发送数据;但另一侧没有关闭连接,仍可以发送数据。已关闭的一侧仍然应接收数据,直至对侧也关闭了连接。
🤔三次握手是否可以关闭连接?
可以的。主机A发出FIN,主机B回复FIN&ACK,然后主机A回复ACK。一般情况,主机B发送FIN和发送ACK是分开发送的。
🤔TIME_WAIT为什么需要等待2MSL?TCP面经
首先,MSL是报文段在网络上存活的最长时间。TIME_WAIT发送的ACK报文可能丢包,此时Receiver将超时重新发送FIN报文,FIN报文最大存活时间MSL; Initiator TIME_WAIT收到FIN报文段后将重新发送ACK,定时器重置。(TIME_WAIT阶段占用端口不可用)。
🤔多核对TCP的影响?
两个瓶颈点:
- 单一的accept队列
- 单一的ListenerHash,EstablishHash
不管多少个并行处理的CPU,在TCP新建连接时,必然要在操作上述两个数据结构时被串行化!
- tcp_v4_rcv(tcp_v6_rcv)接受网络层传递的数据,确认连接已连接
- 处理建立连接过程中的状态机
- 内核处理数据报文
- 发送报文
- tcp_rcv_established 处理数据
- 快路径 直接将数据包送到用户态
- 保存数据到接受队列
- tcp_recvmsg 接收到完整数据
- TODO
- TODO
- TODO
- sock_recvmsg
UDP
是一个简单的面向数据包的通信协议;由于UDP缺乏可靠性且属于连接协议,所以应用程序通常必须容许一些丢失、错误或重复的数据包。
- 报文长度:UDP头和数据共占用长度。
- 校验和:首部和数据一起做校验和。
UDP由于无连接、数据有边界。因此发送速度快,因此应用在在线视频媒体,电视广播和多人在线游戏。
- __udp4_lib_lookup_skb 根据ip:port找对应socket。未找到则丢包。
- 将数据包放入队列,队列满则丢包;
- 命中过滤规则则丢包
- 放到队列尾
- 数据已经准备好等待应用层调用
🤔多核对UDP的影响?
每个UDP Socket都有一个独立的接收队列,多核同时向一个UDP Socket写入数据包时,需要锁保证数据不被覆盖。由于UDP无连接,锁的开销比较低。
🤔UDP接收包大小的不确定性?
UDP是无序的。recvfrom 读取需要设置buffer长度。
- 若长度小于真实数据包长度,则导致读取不完整。UDP数据包理想长度小于MTU(1500-20(IP)-8(UDP)=1472),保守些(<1472)。
- 若长度大于真实数据包长度,则取最小长度读取数据。
🤔UDP实现可靠性的可行性?
TCP UDP Golang 代码示例:
应用层
HTTPS
- TCP 三次握手建立通信连接
- 客户端(浏览器): 发送 Client Hello 消息
客户端支持的TLS版本:TLS1.2
客户端随机值(Client Random)
客户端会话标识
支持的加密套件集合
- 服务端 发送 Server Hello 消息
服务端支持的TLS版本:TLS1.2
服务端随机值(Server Random)
服务端会话标识与客户端一致
服务端选择本次会话的加密算法
- 服务端 发送发送证书
证书存储 公钥 信息
如何验证证书的有效性?X.509
证书链:根证书→二级证书→三级证书→…
证书主要用于识别身份,保护数据传输安全。证书是个好东西,就是贵!!
- 服务侧发送 密钥参数
- 客户端发送 密钥参数
- 客户端发送 改成对称加密
- 服务端发送验证
Client Hello/Server Hello 消息中存在客户端随机数、服务端随机数
Server Key Exchange(可选)/Client Key Exchange(预主密钥)
这些参数使得客户端能够与服务器共同计算出一个共享的秘密密钥,从而用于加密通信。
HTTP2
关系:1个TCP可以跑多个流,1个流可包含多个消息,1个消息可包含多个帧。
基本单位:帧是基本单位
- 二进制协议帧(固定长度9字节)
- 24bit长度,理论最大长度16MB,实际大小限制16KB(可修改)
- 8bit类型,决定帧类型
- 1bit预留,总是0
- 31bit 流标识,http2流标识
- 多路复用
- “数据”可以在多条流上跑
- 头部压缩 HPack
🤔 Http2 Server Push和Websocket SSE(Server Sent event)的区别和联系?
eBPF/XDP
背景
1. 1992年的 USENIX 会议上,The BSD Packet Filter: A New Architecture for User-level Packet Capture ,第一次提到 BSD Packet Filter (简称BPF)这个概念,这篇论文带来了一个革命性技术。正如其名称他是包过滤相关的技术,其基本原理是用户态定义 BPF 字节码来定义过滤表达式,然后传递给内核,再由虚拟机解释运行。(tcpdump)2. 2013年,研究新的SDN方案,Alexei Starovoitov 为 BPF 做了一次革命性的更新,将 BPF 扩展为一个通用的虚拟机,也就是eBPF (extended Berkeley Packet Filter)。eBPF 不仅扩展了寄存器的数量,还在 4.x 内核中将原本单一的数据包过滤事件逐步动态扩展到内核态函数、用户态函数、跟踪点、性能事件(perf_events)以及安全控制领域。
eBPF 使得BPF不再局限于网络栈,而是成为内核的一个顶级系统。这里主要介绍网络侧的应用
挂载点(AF_XDP)
操作 | 含义 |
XDP_PASS | 通过,进入网络栈 |
XDP_DROP | 拒绝,丢包 |
XDP_TX | 转发,本设备 |
XDP_REDIRECT | 转发,另设备 |
工作流
- 编写C程序代码(bpf)
- 通过CLang编译成字节码
- 通过bpf()系统调用
- 验证器检查(大小、循环等检查)
- 通过JIT翻译字节码到机器指令
- 通过ip,tc,bpftool挂载设备
🤔编写C程序依赖elf & zlib
🤔 源码安装libbpf(BPF辅助函数)
🤔 必备工具bpftool(加载eBPF程序)
🤔 bpffs 持久化
XDP, eXpress Data Path, is a high-performance networking technology in the Linux kernel that allows for fast and efficient packet processing at the earliest stage of the networking stack. It operates within the kernel space and provides a programmable interface for handling incoming packets directly on the network interface card (NIC), bypassing much of the traditional networking stack.
eBFP/XDP DDoS
L4防火墙,避免处理DDoS攻击的无效报文。eBPF LPM Map前缀表。应用程序设计bitmap结构。(Match-Action)
XDP_DROP
or XDP_PASS
痛点:不支持分片报文
协议 | 源地址 | 目的地址 | 源端口 | 目的端口 | 动作 |
TCP
UDP
ICMP
GRE | 101.101.102.0/24 10.10.10.10/32 | 101.101.102.0/24 10.10.10.10/32 | 1-65535 22-444 | 1-65535 22-444 | Accept
Drop |
通过构建6张表(协议表、源地址表、目的地址表、源端口表、目的端口表、动作表)
索引 | 协议 | 源IP | 源端口 | 目的IP | 目的端口 | 动作 |
#1 | tcp | 10.10.2.5 | 1-65535 | 10.10.2.4 | 22 | accept |
#2 | tcp | 10.10.2.5 | 1-65535 | 10.10.2.4 | 80 | accept |
#3 | udp | 10.10.2.6 | 4000 | 10.10.2.4 | 3389 | accept |
协议表 | bitmap |
tcp | 110 |
udp | 001 |
源IP表 | bitmap |
10.10.2.5 | 110 |
10.10.2.6 | 001 |
源端口表 | bitmap |
1-65535 | 110 |
4000 | 001 |
动作表 | bitmap |
accept | 111 |
目的IP表 | bitmap |
10.10.2.4 | 111 |
目的端口表 | bitmap |
22 | 100 |
80 | 010 |
3389 | 001 |
工作方式:
- udp 10.10.2.6 4000 10.10.2.4 3389 b=001&001&001&001&111&111=001 ✅
- udp 10.10.2.6 4000 10.10.2.4 3389 b=001&001&001&001&111&111=001 ✅
- udp 10.10.2.6 4001 10.10.2.4 3389 b=001&001&000&111&001&111=000 ❌
- tcp 10.10.2.5 4001 10.10.2.4 3 81 b=110&110&110&000&111&111=000 ❌
加载eBPF map
fw
advancevillage • Updated Jan 11, 2022
挂载eBPF到设备
eBPF/XDP Forward
XDP_TX
or XDP_REDIRECT
网络报文-->网卡-->内核查路由表-->下一跳
[sip, dip, smac, dmac] → [sip, dip, smac1, dmac1]
- BPF_MAP_TYPE_LRU_HASH
- sip uint32 源IP IPv4
- dip uint32 目的IP IPv4
- smac uint64 转发源Mac
- dmac uint64 转发目Mac
- iface uint32 从哪个设备发包
SDN
IPSec
BGP
RDMA的内核旁路机制,允许应用与网卡之间的直接数据读写,将服务器内的数据传输时延降低到1us以下。同时,RDMA的内存零拷贝机制,允许接收端直接从发送端的内存读取数据,极大的减少了CPU的负担,提升CPU的效率。
本质上kernel bypass实现高性能包处理是通过将数据包从kernel移动到user-space
UCX
Loading...
- 关于
黎明
当机器会思考时,会不会失业?!
博客 已经上线🎉
但行耕耘,不问收获 🤪
-- 感谢您的支持 ---