网络抓包
-
网卡相关
抓包命令在后台执行,抓取所有网卡(-i any)的数据,将抓包文件保存到packet.pcap(-w)中,将日志保存到tcpdump.log中:nohup tcpdump -Xvvvnnttt -s 0 -i any -w ./packet.pcap > tcpdump.log 2>&1 &
然后使用Wireshark打开一次执行上面命令的抓包文件packet.pcap。
修改命令,只抓取eth0网卡的所有数据,以太网帧显示Ethernet II:[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 -w ./packet.pcap > tcpdump.log 2>&1 & [2] 959282
-
IP地址相关
抓取所有来自或者发送给固定IP地址的数据包:[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 host 172.19.14.88 -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
抓取所有来自固定IP地址的数据包:
[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 src host 172.19.14.88 -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
抓取所有发送给固定IP地址的数据包:
[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 dst host 172.19.14.88 -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
-
Port相关
抓取所有来自或者发送给固定Port的数据包:[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 port 10000 -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
抓取所有来自或者发送给非指定port的数据包:
[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 not port 10000 -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
抓取所有来自固定port的数据包:
[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 src port 10000 -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
抓取所有发送给固定port的数据包:
[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 dst port 10000 -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
-
协议相关
抓取UDP数据包:[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 udp -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
-
复杂组合
[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 'dst host (192.168.0.2 or 192.168.0.3) \ and dst port 10000 and udp[8:2]=0x0a21 and (udp[10:2]=0x8000 or \ udp[10:1]=0x81)' -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
-
结束抓包,pid是抓包进程id
kill -2 1028902
-
抓包文件切片,一个文件的大小是100MB
[root@iZ7xvhygbzymeb62valoplZ ~]# nohup tcpdump -Xvvvnnttt -s 0 -i eth0 -B 10240 -C 100M -Z root \ -w ./packet.pcap > tcpdump.log 2>&1 & [6] 1028902
细心的读者会发现,上面每个抓包命令的参数都是“-Xvvvnnttt”,下面介绍这个参数组合的意义:
- X:打印每个数据包的数据(包括ASCII码和十六进制值)
- vvv:增加详细程度级别。这里设置为最高级别,提供非常详细的输出。
- nn:不将数据包中的IP地址和端口号转换为与之对应的主机名和服务名称。
- ttt:为每个数据包打印时间戳。
注意:上面导出的packet.pcap都是可以导入wireshark来进行抓包分析的。具体wireshark工具的使用规则大家就Google一下。
网络工具
ethtool工具
ethtool是一个用于配置和显示网络接口的工具,通常用于调整网络接口的参数,以及查看网络接口的状态。
-
网络接口的基本信息
查看网络接口eth0的基本信息,如速率和双工模式等,命令如下:[root@iZ7xvhygbzymeb62valoplZ ~]# ethtool eth0 Settings for eth0:Supported ports: [ ]Supported link modes: Not reportedSupported pause frame use: NoSupports auto-negotiation: NoSupported FEC modes: Not reportedAdvertised link modes: Not reportedAdvertised pause frame use: NoAdvertised auto-negotiation: NoAdvertised FEC modes: Not reportedSpeed: 10000Mb/s # 当前接口的速率是10000Mbps(即1Gbps)Duplex: Full # 当前的双工模式是全双工Port: Twisted PairPHYAD: 0Transceiver: internalAuto-negotiation: offMDI-X: UnknownLink detected: yes
将网络接口eth0的速率设置为1000Mbps,将双工模式设置为全双工,命令如下:
ethtool -s eth0 speed 1000 duplex full
-
网络接口的驱动程序信息
查看网络接口eth0的网卡驱动程序信息,包括网卡驱动程序的名称,版本号和在总线上的位置等信息,命令如下:[root@iZ7xvhygbzymeb62valoplZ ~]# ethtool -i eth0 driver: virtio_net # 网络接口eth0使用的驱动程序是virtio_net version: 1.0.0 # 驱动程序版本号 firmware-version: expansion-rom-version: bus-info: 0000:00:05.0 # 表示网络接口连接在PCI总线上的位置 supports-statistics: yes # 表示该网络接口支持收集统计信息 supports-test: no supports-eeprom-access: no supports-register-dump: no supports-priv-flags: no
-
网络接口的统计信息
查看当前网络接口eth0的统计信息,例如接收和发送数据包的数量等信息。注意:不同网卡驱动程序输出的样式可能不尽相同,本例中的网卡驱动程序是virtio_net。查看统计信息命令及其部分输出结果如下:[root@iZ7xvhygbzymeb62valoplZ ~]# ethtool -S eth0 NIC statistics:rx_queue_0_packets: 777938 # 接收的数据包的数量rx_queue_0_bytes: 96003809 # 接收的字节数tx_queue_0_packets: 769529 # 发送的数据包的数量tx_queue_0_bytes: 99520071 # 发送的字节数
-
网络接口的硬件特性
查看网络接口eth0支持的硬件特性,例如LRO、GRO、TSO和GSO等特性。[root@iZ7xvhygbzymeb62valoplZ ~]# ethtool -k eth0 Features for eth0: rx-checksumming: on [fixed] tx-checksumming: ontx-checksum-ipv4: off [fixed]tx-checksum-ip-generic: ontx-checksum-ipv6: off [fixed]tx-checksum-fcoe-crc: off [fixed]tx-checksum-sctp: off [fixed] scatter-gather: on # 开启了SGtx-scatter-gather: ontx-scatter-gather-fraglist: off [fixed] tcp-segmentation-offload: on # 开启了TSOtx-tcp-segmentation: ontx-tcp-ecn-segmentation: ontx-tcp-mangleid-segmentation: offtx-tcp6-segmentation: on generic-segmentation-offload: on # 开启了GSO generic-receive-offload: on # 开启了GRO large-receive-offload: off [fixed] # 关闭了LRO、fixed表示固定的,不可以修改 rx-vlan-offload: off [fixed] tx-vlan-offload: off [fixed] ntuple-filters: off [fixed] receive-hashing: off [fixed] highdma: on [fixed] rx-vlan-filter: off [fixed] vlan-challenged: off [fixed] tx-lockless: off [fixed] netns-local: off [fixed] tx-gso-robust: on [fixed] tx-fcoe-segmentation: off [fixed] tx-gre-segmentation: off [fixed] tx-gre-csum-segmentation: off [fixed] tx-ipxip4-segmentation: off [fixed] tx-ipxip6-segmentation: off [fixed] tx-udp_tnl-segmentation: off [fixed] tx-udp_tnl-csum-segmentation: off [fixed] tx-gso-partial: off [fixed] tx-sctp-segmentation: off [fixed] tx-esp-segmentation: off [fixed] tx-udp-segmentation: off [fixed] tls-hw-rx-offload: off [fixed] fcoe-mtu: off [fixed] tx-nocache-copy: off loopback: off [fixed] rx-fcs: off [fixed] rx-all: off [fixed] tx-vlan-stag-hw-insert: off [fixed] rx-vlan-stag-hw-parse: off [fixed] rx-vlan-stag-filter: off [fixed] l2-fwd-offload: off [fixed] hw-tc-offload: off [fixed] esp-hw-offload: off [fixed] esp-tx-csum-hw-offload: off [fixed] rx-udp_tunnel-port-offload: off [fixed] tls-hw-tx-offload: off [fixed] rx-gro-hw: off [fixed] tls-hw-record: off [fixed]
-
网络接口的硬中断合并策略
网卡触发硬中断时,CPU会消耗一部分性能来处理上下文切换,以便处理完中断后恢复原来的工作,如果网卡每收到一个包就触发硬中断,则频繁的中断增大了CPU的开销。如果在收到或发送多个数据包再触发硬中断,那么可以大大降低CPU的开销。
查看当前网络接口eth0的硬中断合并策略的命令和输出结果,以及部分字段含义:[root@iZ7xvhygbzymeb62valoplZ ~]# ethtool -c eth0 Coalesce parameters for eth0: Cannot get device coalesce settings: Operation not supported //看来笔者的网卡不支持查看
-
网络接口的Ring Buffer长度
Ring Buffer是一个环形缓存区,数据包的收发都会使用它。当接收数据包时,数据包会经过接收(RX)Ring Buffer,当发送数据包时,数据包会经过发送(TX)Ring Buffer。[root@iZ7xvhygbzymeb62valoplZ ~]# ethtool -g eth0 Ring parameters for eth0: Pre-set maximums: RX: 4096 # 接收Ring Buffer支持的最大长度 RX Mini: 0 RX Jumbo: 0 TX: 4096 # 发送Ring Buffer支持的最大长度 Current hardware settings: RX: 4096 # 接收Ring Buffer的当前长度 RX Mini: 0 RX Jumbo: 0 TX: 4096 # 发送Ring Buffer支持的最大长度
我们可以通过下面的命令将网络接口eth0的接收和发送Ring Buffer的长度都设置为2048,也可以只修改或发送Ring Buffer的长度。
ethtool -G eth0 rx 2048 tx 2048 #同时修改接收和发送Ring Buffer的长度 ethtool -G eth0 rx 2048 #修改接收Ring Buffer的长度 ethtool -G eth0 tx 2048 #修改发送Ring Buffer的长度
-
网络接口的Ring Buffer数量
通过下面的命令可以查看网卡支持的最大Ring Buffer的数量和当前设置的Ring Buffer的数量:[root@iZ7xvhygbzymeb62valoplZ ~]# ethtool -l eth0 Channel parameters for eth0: Pre-set maximums: RX: 0 # 最大接收Ring Buffer的数量,大于0表示可以单独设置 TX: 0 # 最大发送Ring Buffer的数量,大于0表示可以单独设置 Other: 0 Combined: 1 # 最大接收和发送结合的Ring Buffer的数量,即1对Ring Buffer Current hardware settings: RX: 0 # 当前设置的接收Ring Buffer的数量 TX: 0 # 当前设置的发送Ring Buffer的数量 Other: 0 Combined: 1 # 当前设置的接收和发送结合的Ring Buffer的数量,即1对Ring Buffer
上面输出结果中的Combined表示一个接收Ring Buffer和一个发送Ring Buffer结合成一对Ring Buffer,只能一起调整接收和发送Ring Buffer的数量,并且它们的数量始终时相等的,如果RX大于0或者TX大于0,那么表示接收或发送Ring Buffer的数量可以单独设置,它们的数量可以不相等。这里,假设机器的CPU有16个核心,那么将结合的(Combined)Ring Buffer的数量设置成与核心数量一致比较好,每一个核心专门负责处理一对Ring Buffer触发的硬中断。修改Ring Buffer的数量的命令如下:
ethtool -L eth0 combined 16
ifconfig工具
ifconfig工具是一个比较常用的网络工具,笔者经常使用它查看机器上所有网络接口的IP地址。除了查看IP地址,还可以查看网络接口的MAC地址、MTU大小、收包(RX)和发包(TX)数量等统计信息。
[root@iZ7xvhygbzymeb62valoplZ ~]# ifconfig
br-1933dbfb67fa: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 172.26.0.1 netmask 255.255.0.0 broadcast 172.26.255.255inet6 fe80::42:f6ff:fe44:dc70 prefixlen 64 scopeid 0x20<link>ether 02:42:f6:44:dc:70 txqueuelen 0 (Ethernet)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0br-6d45fc43b2e5: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 172.23.0.1 netmask 255.255.0.0 broadcast 172.23.255.255inet6 fe80::42:72ff:feff:32c4 prefixlen 64 scopeid 0x20<link>ether 02:42:72:ff:32:c4 txqueuelen 0 (Ethernet)RX packets 14070 bytes 1609336 (1.5 MiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 9626 bytes 7706427 (7.3 MiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0br-d7a979790452: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 172.25.0.1 netmask 255.255.0.0 broadcast 172.25.255.255inet6 fe80::42:54ff:fec5:58f7 prefixlen 64 scopeid 0x20<link>ether 02:42:54:c5:58:f7 txqueuelen 0 (Ethernet)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255ether 02:42:7c:e7:b9:61 txqueuelen 0 (Ethernet)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 172.19.14.88 netmask 255.255.192.0 broadcast 172.19.63.255inet6 fe80::216:3eff:fe05:229b prefixlen 64 scopeid 0x20<link>ether 00:16:3e:05:22:9b txqueuelen 1000 (Ethernet)RX packets 2162937 bytes 264114050 (251.8 MiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 2142232 bytes 275933236 (263.1 MiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536inet 127.0.0.1 netmask 255.0.0.0inet6 ::1 prefixlen 128 scopeid 0x10<host>loop txqueuelen 1000 (Local Loopback)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0veth22ba364: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet6 fe80::6848:5dff:fe04:8d3e prefixlen 64 scopeid 0x20<link>ether 6a:48:5d:04:8d:3e txqueuelen 0 (Ethernet)RX packets 137637 bytes 43232670 (41.2 MiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 151317 bytes 40462854 (38.5 MiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0veth698b2ae: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet6 fe80::d4cf:98ff:fe23:2062 prefixlen 64 scopeid 0x20<link>ether d6:cf:98:23:20:62 txqueuelen 0 (Ethernet)RX packets 124332 bytes 37759453 (36.0 MiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 131739 bytes 34996011 (33.3 MiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0veth716fb4b: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet6 fe80::d4fb:bdff:fefe:df60 prefixlen 64 scopeid 0x20<link>ether d6:fb:bd:fe:df:60 txqueuelen 0 (Ethernet)RX packets 14070 bytes 1609336 (1.5 MiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 9626 bytes 7706427 (7.3 MiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0veth75c6180: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet6 fe80::80c0:66ff:fe5c:9629 prefixlen 64 scopeid 0x20<link>ether 82:c0:66:5c:96:29 txqueuelen 0 (Ethernet)RX packets 24 bytes 2190 (2.1 KiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 58 bytes 4738 (4.6 KiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0vethdb76b24: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet6 fe80::cc55:69ff:fe0a:b406 prefixlen 64 scopeid 0x20<link>ether ce:55:69:0a:b4:06 txqueuelen 0 (Ethernet)RX packets 169388 bytes 46286977 (44.1 MiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 152822 bytes 45734588 (43.6 MiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0vethf4a2ef1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet6 fe80::21:eaff:fee2:c1de prefixlen 64 scopeid 0x20<link>ether 02:21:ea:e2:c1:de txqueuelen 0 (Ethernet)RX packets 116931 bytes 9087846 (8.6 MiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 91975 bytes 25949926 (24.7 MiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
除了查看信息,ifconfig工具还可以对IP地址、MAC地址和MTU等信息进行修改:
ifconfig eth0 mtu 1000
ip工具
ip工具比ifconfig工具更加强大和全面,它整合了包括ifconfig工具在内的很多工具。例如,ip route可以用于管理路由信息,添加、删除、编辑路由表等。ip link主要用于查看网络接口的状态,配置网络接口参数、启动或关闭网络接口等,类似ifconfig工具。
下面演示如何添加一个虚拟网络接口eth1:
-
创建一个虚拟网络接口eth1:
[root@iZ7xvhygbzymeb62valoplZ ~]# ip link add eth1 type dummy
-
启用网络接口eth1:
[root@iZ7xvhygbzymeb62valoplZ ~]# ip link show eth1 23: eth1: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 66:c1:0f:af:48:75 brd ff:ff:ff:ff:ff:ff [root@iZ7xvhygbzymeb62valoplZ ~]# ip link set eth1 up [root@iZ7xvhygbzymeb62valoplZ ~]# ip link show eth1 23: eth1: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
-
配置网络接口eth1的ip地址:
[root@iZ7xvhygbzymeb62valoplZ ~]# ip addr add 172.19.14.88/24 dev eth1
-
验证网卡接口eth1是否添加成功:
[root@iZ7xvhygbzymeb62valoplZ ~]# ip link show eth1
-
删除指定网络接口
[root@iZ7xvhygbzymeb62valoplZ ~]# ip delete eth1
nc工具
nc是一个网络连接工具,拥有网络界的”瑞斯军刀“美誉,它的用途非常广泛。可以用来在两台计算机之前建立网络连接,实现任意TCP或UDP端口的监听,也可以作为客户端发起TCP或UDP连接。
-
机器A通过nc命令使用TCP/UDP协议监听端口1000来接收数据。
[root@iZ7xvhygbzymeb62valoplZ ~]# nc -l 1000 #接收TCP数据包 [root@iZ7xvhygbzymeb62valoplZ ~]# nc -ul 1000 # 接收UDP数据包
-
机器B通过nc命令向机器A的端口1000发送一个TCP/UDP数据包。
[root@iZ7xvhygbzymeb62valoplZ ~]# echo "hello" | nc -u 172.19.14.88 1000 # 发送TCP数据包 [root@iZ7xvhygbzymeb62valoplZ ~]# echo "hello" | nc 172.19.14.88 1000 # 发送UDP数据包
在实际工作中,有时会遇到两台机器直接进行文件传输的情况,下面是使用nc命令传输文件的例子:
nc -l 1000 > 1.txt # 机器A接收文件
nc 172.19.14.88 1000 <1.txt # 机器B发送文件
网卡的特性
-
LRO
LRO是由网卡实现的一种优化网络传输的技术。LRO的基本思想是将多个小数据包合并为一个大数据包,然后将大数据包递交到网络栈,而网络栈只需处理一次数据包,意在降低多次处理小数据包时涉及的中断上下文切换和网络栈的开销。
然而并非所有的网卡都支持LRO,这取决于网卡的型号和制造商。另外,因为数据包需要等待合并,所以LRO可能会导致一些额外延迟。在实际应用中,可以根据具体情况决定是否通过配置启用LRO。ethtool -K eth0 large-receive-offload on # 启用LRO ethtool -K eth0 large-receive-offload off # 关闭LRO
-
CRO
CRO是LRO的软件实现。即使网卡不支持LRO,也可以使用GRO支持这个功能。GRO的主要目标仅仅是降低网络栈的开销,不能像LRO那样可以降低中断上下文切换的开销,这是因为GRO已经处于软中断上下文中。
与LRO相同,GRO也有可能引入一些延迟,并且它并非被所有内核和网络协议栈所支持。ethtool -K eth0 generic-receive-offload on # 启用GRO ethtool -K eth0 generic-receive-offload off # 关闭GRO
-
TSO
TSO是一种由网卡支持的优化网络传输的技术。TSO主要用于发送端,它的目标是将较大的TCP数据包在网卡分片为多个小数据包,然后发送出去,而不需要在网络协议栈上进行分片,然后每个小数据包都经过一遍网络栈的处理。TSO降低了CPU处理大量小数据包的开销,提高了网络传输的效率。ethtool -K eth0 tcp-segmentation-offload on # 启用TSO ethtool -K eth0 tcp-segmentation-offload off # 关闭TSO
-
GSO
GSO是一种在软件层面实现的网络优化技术。与GRO相反,与TSO类似,GSO也主要用于发送端,它的目标是将较大的数据包的分片操作尽量靠近网卡的驱动程序,然后将分片后的小数据包逐个发送给网卡驱动程序,尽量降低网络栈多次处理小数据包的开销。ethtool -K eth0 generic-segmentation-offload on # 启用GSO ethtool -K eth0 generic-segmentation-offload off # 关闭GSO
网络栈的扩展
RSS
RSS是一种在网卡和驱动程序层面实现的提升多核系统网络接收性能的技术。它允许网卡将收到的数据包根据源IP地址、目标IP地址、源端口和目标端口等信息”哈希“分散到多个接收Ring Buffer,然后触发硬中断让系统中多个CPU并行处理,提高网络吞吐量。
-
数据包中参与哈希计算的字段
通过下面命令可以分别查看TCP和UDP数据包参与哈希计算的字段:ethtool -n eth0 rx-flow-hash tcp4 ethtool -n eth0 rx-flow-hash udp4
通过下面命令可以修改TCP数据包中参与哈希计算的字段(同样适用于UDP数据包):
ethtool -N eth0 rx-flow-hash tcp4 sdfnvtmr
参数sdfnvtmr中的字母的含义如下,可以有不同的组合(顺序无关):
- s:数据包的源IP地址。
- d:数据包的目标IP地址。
- f:数据包的源端口。
- n:数据包的目标端口。
- v:数据包的VLAN ID(标签)。
- t:数据包的第三层(Layer 3 Protocol)协议,例如IPv4、IPv6等
- m:数据包的目标MAC地址
- r:数据包的丢弃标志,用于将具有相同丢弃标志的数据包分配到相同的接收队列中。
-
调整接收Ring Buffer的权重
计算完数据包的哈希值后,通过查询事先配置好的哈希表,可以将数据包传递到对应的接收Ring Buffer中。使用下面的命令查看哈希表:ethtool -x eth0
RPS
RPS是RSS的软件实现,它不依赖硬件,因此任何网卡都可以启用他,包括单队列和多队列网卡。启用RPS的单队列网卡可以提高传输效率,而多队列网卡在硬中断不均匀的情况下也可以利用RPS来提高效率。RPS首先根据数据包的哈希值将数据包分发到其他CPU(也有可能是本CPU)的后备队列中,然后给目标CPU触发一个IPI中断。最后目标CPU处理它们各自的后备队列中的数据包。这就引入了CPU间的中断处理和数据传输,进而导致RPS使用更多的CPU资源,因此RPS默认是关闭,需要谨慎使用。
[root@iZ7xvhygbzymeb62valoplZ ~]# cat /proc/softirqs //查看当前系统中所有软中断的输出结果CPU0 CPU1 HI: 0 0TIMER: 5517535 5575668NET_TX: 15 17NET_RX: 3204655 319992BLOCK: 0 7194020IRQ_POLL: 0 0TASKLET: 166 52SCHED: 7616597 7581816HRTIMER: 0 0RCU: 26767198 26814773
通过下面的命令可以看到,笔者的服务器上只有一个接收队列和一个发送队列
[root@iZ7xvhygbzymeb62valoplZ ~]# ls /sys/class/net/eth0/queues
rx-0 tx-0
通过下面的命令查看接收队列rx-0是否开启了RPS:
[root@iZ7xvhygbzymeb62valoplZ ~]# cat /sys/class/net/eth0/queues/rx-0/rps_cpus
0
输出中的0代表没有开启RPS。我们可以使用下面的命令将接收队列rx-0
上触发的NET_RX软中断均匀分给CPU 0和CPU 2进行处理:
echo 2 > /sys/class/net/eth0/queues/rx-0/rps_cpus
上面命令中的2是16进制的值,转换成二进制值是101,每个比特位对应一个CPU,因此10对应cpu0和cpu1。
RFS
RFS通常和RPS一起使用。RPS只是将数据包根据哈希值分发到不同的cpu上处理,虽然达到了cpu的负载均衡,但是又引出一个新问题,那就是当前执行收发数据包的应用程序进程的CPU可能和数据包”哈希“到的CPU不是同一个,进而造成cpu缓存命中率降低。
因此引入了RFS来解决上面的问题,RFS保证在一段时间内相同数据流上的数据包都由同一个cpu处理,以此来提高cpu缓存命中率。
XPS
XPS是一种在多队列设备上智能选择发送队列的机制。传输方向正好与RPS相反。XPS选择硬件发送队列有两种方式,一种是将CPU映射到发送队列,另一种是将接收队列映射到发送队列。通常使用基于cpu的映射方式。
- 将cpu映射到发送队列
系统管理员可以通过配置文件xps_cpus将一些cpu映射到一些发送队列,通常是一对一映射。这样做的好处是降低了cpu间竞争同一个发送队列锁的概率,进而减少了cpu的开销。如果每个cpu都有自己的发送队列,那么完全不需要竞争。 - 将接收队列映射到发送队列
系统管理员可以通过配置文件xps_rxqs 将一个(些)接收队列映射到一个
(些)发送队列,通常也是一对一映射。这样做有两个好处:
- 网卡会将具有相同四元组的数据包“哈希”到相同的接收队列,根据接收队列到发送队列的映射关系,相同四元组连接上发送的数据包都会被发送到相同的或者一组发送队列,在一定程度上防止了数据包的乱序。
- 当传输完成后执行清理工作时,网卡触发的硬中断会发送给相同的或者一些CPU,这个(些)CPU可以集中处理大量的清理工作,间接减少CPU的硬中断次数。
上面介绍的机制都不是独立使用的,而是在不同场景下,几种机制配合使用才会达到最佳效果。目前大部分网卡都支持多队列,也就支持RSS功能。在多队列网卡情况下,通常会配合使用RSS、硬中断的CPU亲和性(稍后详细介绍)
和XPS功能。
例如,将每对结合的收发队列对应的硬中断分别唯一绑定到一个CPU,然后将每个CPU唯一映射到硬中断绑定该CPU上的发送队列,这样一来,处理收包软中断和发送完成软中断的内核进程,以及发包进程都在一个CPU上执行
可以提升CPU的缓存命中率,以及减少进程上下文切换的开销。有些网卡驱动程序在软中断处理函数中既执行收包工作,也执行发送完成的清理工作,因此当网卡触发收包硬中断或者发包完成的硬中断时,顺便也处理了另一方面的工作,减少了进程上下文切换,并且降低了数据包的延迟。
硬中断的负载均衡
正如我们前面看到的那样,通常情况下,一个接收Ring Buffer会和一个发送Ring Buffer 结合成一对 Ring Buffer,内核为每对 Ring Buffer都分配一个衡断号,当一对 Ring Buffer 上接收或发送了数据包时,网卡既可以单独触发
CPU的硬中断,也可以根据数锯包的哈希值触发多个CPU的硬中断,但每次给一个CPU触发硬中断。这里固定给某个(些) CPU触发硬中断的方式就是所谓的硬中断的CPU亲和性。
我们可以使用cat/proc/interrupts 命令查看每对 RingBuffer的硬中新以及它给所有CPU触发硬中断的次数:
[root@iZ7xvhygbzymeb62valoplZ ~]# cat /proc/interruptsCPU0 CPU1 0: 6 0 IO-APIC 2-edge timer1: 9 0 IO-APIC 1-edge i80424: 767 0 IO-APIC 4-edge ttyS08: 0 0 IO-APIC 8-edge rtc09: 0 0 IO-APIC 9-fasteoi acpi10: 0 0 IO-APIC 10-fasteoi virtio311: 21 0 IO-APIC 11-fasteoi uhci_hcd:usb112: 0 16 IO-APIC 12-edge i804214: 0 0 IO-APIC 14-edge ata_piix15: 0 0 IO-APIC 15-edge ata_piix24: 0 0 PCI-MSI 65536-edge virtio1-config25: 0 9118070 PCI-MSI 65537-edge virtio1-req.026: 0 0 PCI-MSI 49152-edge virtio0-config27: 30 0 PCI-MSI 49153-edge virtio0-virtqueues28: 0 0 PCI-MSI 81920-edge virtio2-config29: 2905671 1 PCI-MSI 81921-edge virtio2-input.030: 1 702923 PCI-MSI 81922-edge virtio2-output.0
NMI: 0 0 Non-maskable interrupts
LOC: 44571601 44606680 Local timer interrupts
SPU: 0 0 Spurious interrupts
PMI: 0 0 Performance monitoring interrupts
IWI: 10826548 10435490 IRQ work interrupts
RTR: 0 0 APIC ICR read retries
RES: 33561049 31051987 Rescheduling interrupts
CAL: 53281 57083 Function call interrupts
TLB: 29029 38505 TLB shootdowns
TRM: 0 0 Thermal event interrupts
THR: 0 0 Threshold APIC interrupts
DFR: 0 0 Deferred Error APIC interrupts
MCE: 0 0 Machine check exceptions
MCP: 98 98 Machine check polls
HYP: 0 0 Hypervisor callback interrupts
HRE: 0 0 Hyper-V reenlightenment interrupts
HVS: 0 0 Hyper-V stimer0 interrupts
ERR: 0
MIS: 0
PIN: 0 0 Posted-interrupt notification event
NPI: 0 0 Nested posted-interrupt event
PIW: 0 0 Posted-interrupt wakeup event
根据上面的输出结果,可以明显看出,没对Ring Buffer触发的硬中断只发送给某一个固定的CPU,说明已经将每对Ring Buffer的硬中断绑定到了一个CPU上。
我们有两种发送在使每个CPU上触发的硬中断负载均衡,一种是通过手动修改每个硬中断的cpu亲和性配置文件,另一种是通过irqbalance功能自动完成硬中断的负载均衡。
硬中断的亲和性
[root@iZ7xvhygbzymeb62valoplZ ~]# cat /proc/irq/30/smp_affinity //查看硬中断号30绑定到哪个cpu
2 //16进制,10,第2位是1,说明绑定到了cpu1上。
echo 3> /proc/irq/30/smp_affinity #修改亲和性,将其绑定到cpu0和cpu1上。
irqbalance功能
sudo systemctl start irqbalance 启用irqbalance功能自动将硬中断平衡分发到各个cpu上,以提高系统性能和吞吐量。该命令在重启机器后会失效sudo systemctl enable irqbalance #系统启动时自动启用irqbalance功能
如果即想使用irqbalance功能,又想使用硬中断的cpu亲和性,那么必须禁止irqbalance功能对设置了cpu亲和性的硬中断号进行负载均衡,否则irqbalance功能将忽视硬中断的cpu亲和性。例如,禁止irqbalance功能对硬中断号59和60进行负载均衡的命令如下:
irqbalance --banirq=59 --banirq=60
IRQ 59 was BANNED
IRQ 60 was BANNED