网络包排错指南-类linux 平台

网络包排错指南-类linux 平台

背景信息

最近一直在测试k8s,若是你了解或者解接触过docker,那你必定知道docker 相关的网络很大部分在桥接、路由、Iptables 上作文章。若是你凑巧接触过k8s,而且了解其后面的原理,那你必定知道kube-proxy 把iptables 玩的简直要飞起来。固然你可能会想到一些排错工具,好比我以前经常使用的抓包工具,或者路由跟踪工具,但这些工具在目前这样复杂的环境下,是不太趁手的,特别是包在本机的多个网卡或者虚拟网卡里转来转去,还有不少个iptables策略,路由等让包在内核空间中转来转去。抓包工具抓不到这些信息,traceroute 跟踪路由时你会发现你须要跟踪一个src,dst 还有port的包的路由信息是没有法达成的。html

这里介绍一些新的排错工具:linux

  • IPTables 跟踪排错
  • 本地路由 排错
  • 一些网络相关的内核参数设置。

Iptables 跟踪排错

说到Iptables 排错,我不得不拿出这张逻辑很是清晰的图出来,建议Iptables 排错时经常对照下这张图,看下数据包的传递路径。在我以前的IPtables 知识范畴里,我觉得它多个表之间传递时是没有路由选择这个操做的,结果实际的排错加上这样图来看。原来在不一样的table 之间可能通过Routing decision. git

请参考个人这篇K8s Issue 中的排错过程。github

Netfilter-packet-flow.svg

而后我不得不说下Iptables 的TRACE Target,没有了解到这个Target以前,我用LOG Target,结果发现要写好多个IPtables你也不必定能跟踪的全每一个包通过的策略,以及策略如何处理的。docker

我目前演示的在ubuntu 上面:ubuntu

######### 检查是TRACE相关的mod 是否载入
modprobe nf_log_ipv4

########## TRACE Target 只能应用于RAW Table

sudo iptables -t raw -I PREROUTING -p tcp -m tcp --dport 8081   -j TRACE
sudo iptables -t raw -I OUTPUT -p tcp -m tcp --dport 8081   -j TRACE

########### grep TRACE in /var/log/kern.log

grep TRACE /var/log/kern.log

ubuntu@ceph3:~$ grep TRACE /var/log/kern.log|grep 2213090174
May  8 16:30:29 ceph3 kernel: [324781.838361] TRACE: raw:OUTPUT:policy:2 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.43.206.251 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838389] TRACE: nat:OUTPUT:rule:1 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.43.206.251 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838417] TRACE: nat:KUBE-SERVICES:rule:9 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.43.206.251 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838439] TRACE: nat:KUBE-SVC-ZP4VKUJYTBCROZYY:rule:1 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.43.206.251 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838454] TRACE: nat:KUBE-SEP-OR6JECCPPINGGGRC:rule:2 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.43.206.251 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838479] TRACE: filter:OUTPUT:rule:1 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838493] TRACE: filter:KUBE-SERVICES:return:2 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838505] TRACE: filter:OUTPUT:rule:2 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838518] TRACE: filter:KUBE-FIREWALL:return:2 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838531] TRACE: filter:OUTPUT:rule:4 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838551] TRACE: filter:OUTPUT:policy:6 IN= OUT=enp3s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838564] TRACE: nat:POSTROUTING:rule:1 IN= OUT=enp5s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838577] TRACE: nat:KUBE-POSTROUTING:return:2 IN= OUT=enp5s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838589] TRACE: nat:POSTROUTING:rule:8 IN= OUT=enp5s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000
May  8 16:30:29 ceph3 kernel: [324781.838609] TRACE: nat:POSTROUTING:policy:10 IN= OUT=enp5s0 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=57266 DF PROTO=TCP SPT=18130 DPT=8081 SEQ=2213090174 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04D5CCCC0000000001030307) UID=1000 GID=1000

解释一下,每一个trace 会记录表名字好比raw:OUTPUT:policy:2或者nat:OUTPUT:rule:1,代表为Table:Chain:显式策略为Rule,Table默认策略为Policy:rule 编号。我通常用grep ID=57266 这种方法去过滤同一个包。bash

以上都抓取的iptables 的日志,若是中间遇到路由问题呢,好比我这个问题包日志以下,包到了nat:PREROUTING:policy:3 就没有下文了,原本应该继续进mangle:INPUT 或者filter:INPUT,结果都没有,参考以上数据包图,能够发现这里有个route decision的过程。那么接下来我看看若是排除本地路由的问题。网络

ubuntu@ceph2:~$ grep TRACE /var/log/kern.log|grep 1726587944
May  8 15:51:07 ceph2 kernel: [309854.514762] TRACE: raw:PREROUTING:policy:2 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)
May  8 15:51:07 ceph2 kernel: [309854.514799] TRACE: nat:PREROUTING:rule:1 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)
May  8 15:51:07 ceph2 kernel: [309854.514841] TRACE: nat:KUBE-SERVICES:rule:13 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)
May  8 15:51:07 ceph2 kernel: [309854.514861] TRACE: nat:KUBE-NODEPORTS:return:1 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)
May  8 15:51:07 ceph2 kernel: [309854.514881] TRACE: nat:KUBE-SERVICES:return:14 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)
May  8 15:51:07 ceph2 kernel: [309854.514897] TRACE: nat:PREROUTING:rule:2 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)
May  8 15:51:07 ceph2 kernel: [309854.514914] TRACE: nat:DOCKER:return:2 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)
May  8 15:51:07 ceph2 kernel: [309854.514930] TRACE: nat:PREROUTING:policy:3 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)

本地路由 排错

通常状况下本地路由并无多少条,因此通常传统方法是逐条对下路由条目,而后人工判断最终会丢到那里,若是没有发现路由能处理,就会被DROP了。新版本的linux 上用ip rule 和ip route 显示和操做路由表,ip 属于iproute2 包中的套件,后面大体看了下文档,才发现有种还有这种操做的的感受。tcp

######## ip rule to list ip route tables
ubuntu@ceph2:~$ ip rule
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

######## 这里有三个table ,先查0编号的local,而后查32766的main,而后查32767的default
######## 列出每一个table 中的rule
ubuntu@ceph2:~$ ip route list table local
broadcast 10.0.1.0 dev enp5s0  proto kernel  scope link  src 10.0.1.12
local 10.0.1.12 dev enp5s0  proto kernel  scope host  src 10.0.1.12
broadcast 10.0.1.255 dev enp5s0  proto kernel  scope link  src 10.0.1.12
local 10.42.2.0 dev flannel.1  proto kernel  scope host  src 10.42.2.0
broadcast 10.42.2.0 dev cni0  proto kernel  scope link  src 10.42.2.1
local 10.42.2.1 dev cni0  proto kernel  scope host  src 10.42.2.1
broadcast 10.42.2.255 dev cni0  proto kernel  scope link  src 10.42.2.1
broadcast 127.0.0.0 dev lo  proto kernel  scope link  src 127.0.0.1
local 127.0.0.0/8 dev lo  proto kernel  scope host  src 127.0.0.1
local 127.0.0.1 dev lo  proto kernel  scope host  src 127.0.0.1
broadcast 127.255.255.255 dev lo  proto kernel  scope link  src 127.0.0.1
broadcast 172.17.0.0 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown
local 172.17.0.1 dev docker0  proto kernel  scope host  src 172.17.0.1
broadcast 172.17.255.255 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown
broadcast 192.168.235.0 dev enp3s0  proto kernel  scope link  src 192.168.235.12
local 192.168.235.12 dev enp3s0  proto kernel  scope host  src 192.168.235.12
broadcast 192.168.235.255 dev enp3s0  proto kernel  scope link  src 192.168.235.12

ubuntu@ceph2:~$ ip route list table main
default via 192.168.235.2 dev enp3s0 onlink
10.0.1.0/24 dev enp5s0  proto kernel  scope link  src 10.0.1.12
10.42.0.0/24 via 10.42.0.0 dev flannel.1 onlink
10.42.1.0/24 via 10.42.1.0 dev flannel.1 onlink
10.42.2.0/24 dev cni0  proto kernel  scope link  src 10.42.2.1
10.42.3.0/24 via 10.42.3.0 dev flannel.1 onlink
10.42.4.0/24 via 10.42.4.0 dev flannel.1 onlink
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 linkdown
192.168.235.0/24 dev enp3s0  proto kernel  scope link  src 192.168.235.12

ubuntu@ceph2:~$ ip route list table default
ubuntu@ceph2:~$

前面说了通常方法是逐条对路由来看路由条目对特定的包是否有规则对应,但这种方法须要你对路由规则很是熟悉,并且人工容易判断漏。那么这里介绍一个测试路由规则的命令ip route getide

###### 偷懒摘抄下man 8 ip 里的说明
ip route get - get a single route

this command gets a single route to a destination and prints its contents exactly as the kernel sees it.

to ADDRESS (default) #the destination address.
from ADDRESS #the source address.
tos TOS
dsfield TOS # TOS=the Type Of Service.
iif NAME #the device from which this packet is expected to arrive.
oif NAME #force the output device on which this packet will be routed.

以上面iptables 跟踪部分的这条日志为例

May  8 15:51:07 ceph2 kernel: [309854.514930] TRACE: nat:PREROUTING:policy:3 IN=enp5s0 OUT= MAC=00:23:7d:5b:96:ec:00:21:5a:ef:39:fe:08:00 SRC=192.168.235.13 DST=10.0.1.12 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=28265 DF PROTO=TCP SPT=14024 DPT=8081 SEQ=1726587944 ACK=0 WINDOW=29200 RES=0x00 SYN URGP=0 OPT (020405B40402080A04CCCA410000000001030307)

假设咱们要判断这个包的路由选择会匹配哪条路由,咱们能够下面命令来看,初一看结果会第一反应是否是我命令语法有问题报错呢?来看看我当时参考了rp_filter_kernel_setting后的运行结果

ubuntu@ceph2:~$ ip route get from 192.168.235.13 to 10.0.1.12 iif enp5s0 tos 0x00
RTNETLINK answers: Invalid cross-device link

######### change rp_filter kernel setting
ubuntu@ceph2:~$ sudo bash
root@ceph2:~# echo 2 > /proc/sys/net/ipv4/conf/default/rp_filter
root@ceph2:~# echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter

######### Ok 在更改rp_filter 内核参数后,咱们的一样的命令有匹配结果了。

root@ceph2:~# ip route get from 192.168.235.13 to 10.0.1.12 iif enp5s0 tos 0x00
local 10.0.1.12 from 192.168.235.13 dev lo
    cache <local>  iif enp5s0
root@ceph2:~#

到这里我想你能够再尝试一些其余的ip route get 命令来连连手,看看输出结果,好比

ubuntu@ceph2:~$ ip route get from 172.18.0.3 to 10.0.1.12
RTNETLINK answers: Invalid argument # 本机根本无法从172.18.0.3 路由到10.0.1.12
ubuntu@ceph2:~$ ip route get from 192.168.235.3 to 10.0.1.12
RTNETLINK answers: Invalid argument # 本机也无法从192.168.235.3 路由到10.0.1.12
ubuntu@ceph2:~$ ip route get from 192.168.235.12 to 10.0.1.12 #从本地的一个卡为192.168.235.12 能够从lo 上路由到10.0.1.12
local 10.0.1.12 from 192.168.235.12 dev lo
    cache <local>
ubuntu@ceph2:~$ ip route get from 192.168.235.12 to 10.0.1.13 #从本地的一个卡为192.168.235.12 能够从enp5s0 上路由到10.0.1.13(另一个主机)
10.0.1.13 from 192.168.235.12 dev enp5s0
    cache

最后咱们详细解读下路由规则的显示意思,具体能够参考【iproute2 doc】

以这条比较长的broadcast 10.0.1.0 dev enp5s0 proto kernel scope link src 10.0.1.12为例

  • broadcast 10.0.1.0 第一个为路由类型,能够为broadcast,unicast,local等等,若是不写,则为unicast,10.0.1.0 为目的网络。
  • dev enp5s0 这表明出去的时候走网卡enp5s0
  • via 10.42.3.0 你可能在有些规则中看到这句,表明下一跳网关是10.42.3.0
  • proto kernel 路由协议是kernel,由kernel 生成。
  • scope link 该地址只在该link 上有效
  • src 10.0.1.12 源Ip为10.0.1.12, 这里的10.0.1.12 必须在本地的网卡地址上能找到
  • onlink 伪装下一跳的网关在这个link上 。

一些网络相关的内核参数设置

OK,快到最后不得不提下linux 的内核参数设置,这些参数能在内核中能够设置,每每是提炼了又提炼的精华部分。那么问题来了?

  • Q : 我怎么知道哪些参数是我须要的呢?

    A : linux的内核文档中会对这些参数加以详细描述,所以咱们能够阅读内核文档,好比和IP相关的参数,来寻找咱们可能须要的参数,个人思路是经过本身以为有可能的内核参数名去搜索互联网,而后看结果中别人使用这个参数具体解决了什么问题。

  • Q: 那里找到linux 内核文档?

    A: 以ubuntu 为例,linux-doc 是当前kernel的文档包,安装后的文件在/usr/share/doc/linux-doc/主目录下,能够 dpkg -L linux-doc查看寻找所需文档。好比zcat /usr/share/doc/linux-doc/networking/ip-sysctl.txt.gz能够阅读网络相关内核参数的文档。

几个重要的内核参数

  • rp_filter 设置为2时会针对全部网卡匹配包的src,若是匹配则路由,设置为1时,若是包通过的网卡发现返回路径不是最优,则丢弃包。

    net.ipv4.conf.default.rp_filter = 2

    net.ipv4.conf.all.rp_filter=2

  • log_martians Boolean 设置为enable 时,上面这种被内核认为是不可能的地址的时候,能够在内核日志中记录信息。

其余补充

  • 跨主机时,抓包工具是补充。
  • 知识须要扩散、深挖,欢迎补充。
相关文章
相关标签/搜索