IPv6协议栈里, 当一个网口被down之后,网口上对应的IP地址也会一起被flush掉。
面对IPv6跟IPv4不同的行为方式,内核提供了一个规避的开关。
在4.6内核之后提供了一个开关,用来避免IPv6地址别清理掉。
这个开关既有全局的设置,也有每个interface粒度的单独开关。
1 | keep_addr_on_down - INTEGER |
IPv6协议栈里, 当一个网口被down之后,网口上对应的IP地址也会一起被flush掉。
面对IPv6跟IPv4不同的行为方式,内核提供了一个规避的开关。
在4.6内核之后提供了一个开关,用来避免IPv6地址别清理掉。
这个开关既有全局的设置,也有每个interface粒度的单独开关。
1 | keep_addr_on_down - INTEGER |
这里以tcpdump(PF_PACKET)为例,结合函数调用关系说明,
skb是如何被当做ctx参数传递给bpf程序的
注: 内核版本v6.6
1 | --> packet_rcv |
以xdp SKB模式为例,
1 | --> bpf_prog_run_generic_xdp |
xdp在内核里的有三个关键步骤:
load: 加载到内核attach: 绑定到一个网口run:网口收包时候,调用并执行bpf progload加载: 通过ebpf系统调用, 把prog加载到内核fd = sys_bpf(BPF_PROG_LOAD, attr, size);
bfp_prog结构体用以存储bpf prog。bpf_check检查prog程序的安全性和合法性。bpf_prog_select_runtime指定bpf prog对应的执行函数bpf_func这个字段里。这里的function最终指向通用的bfp run函数___bpf_prog_run。___bpf_prog_run这个具体封装和实现见另外一篇文章。attach绑定: 将prog程序绑定到一个特定的网口的struct net_device上libpf函数do_attach将上一步加载在内核里的prog跟一个网口绑定, 具体实现是通过下发netlink命令。
这是个generic类型的netlink命令,最终通过dev_change_xdp_fd将prog挂载到对应netdev下面。
tcpdump在网口直接抓包和读取pcap文件两种场景下,同一个filter表达式icmp,对vlan报文有不同的处理结果。
【Q1】:在读取pcap文件时候,为什么不能读取到vlan报文呢?
内核协议栈:
过滤条件 icmp:tcpdump 解析出来的 ebpf 指令,是要求报文是eth+IP+ICMP格式。通过ETH/IP 头里的协议类型分别做了限制: IPv4(0x0800)+ICMP(1)。
在网口上抓包:内核代码里,在tcpdump抓包过滤的钩子函数之前,会把报文的vlan解析并剔除掉。被剥离的vlan头信息,被保存到skb的metadata里了。所以 tcpdump(af_packet)在内核里,通过钩子函数运行过滤条件的 ebpf 指令时,被处理的报文已经不带vlan头了。因此只要满足icmp 头,带和不带 vlan头的报文都会被抓取到。
读取pcap文件:tcpdump 直接读取文件内容,vlan头并没有被剥离掉,所以vlan报文不满足过滤条件(eth+IP+ICMP),被丢弃了。
国外已经有人发现并分析过这个问题:BPF and tcpdump
接上篇文章,我们提到在网口eth1 上增加一个 ip 地址,内核会生成 3 个路由。
为什么需要使用local/main 两个路由表呢? ip地址引发的路由变化或者手动添加一个目的网段的路由,这些场景看起来,完全可以使用一个路由表就能满足场景需求。那内核为什么要支持多个路由表?
其中一个场景就是 策略路由。
比如公司有两个网关, 其中一个网关GW1,网络质量比较高。我们希望对网络质量有较高要求的个别网段的外访流量,走GW1.其他网段都用 GW2.
这时,我们就不能仅仅依赖目前 IP 去做路由。我们需要:
那下面详细展开介绍下策略路由及内核实现。
策略路由是一个按优先级排列的规则链表。路由查找时,按优先级顺序遍历链。
每个策略路由规则,由match+action两部分组成。 match 也被称为SELECTOR。
ip rule show命令我们可以看到每条rule对应的优先级。添加rule时候,默认的是当前除0以外最高优先级的值-1, 即默认新建的rule优先级高。这部分用法详见ip rule命令帮助手册,https://man7.org/linux/man-pages/man8/ip-rule.8.html
1 | ip netns exec ns1 ip l s veth1 down |