从内网第二网关配置浅谈 ARP Flux 问题

TLDR :在同一个广播域内存在多个路由器/转发器如 VPN 网关、DR 模式的 IPVS 时,需注意内网主机的 ARP 配置,否则默认配置下 Linux 内核会使用所有可用 IP 进行 ARP ,可能引起被称为 ARP Flux 的混乱现象,一个典型现象为:网卡会使用绑定在其他网卡上的 IP 地址进行收发包。net.ipv4.conf.all.arp_ignore,net.ipv4.conf.all.arp_announce,net.ipv4.conf.all.arp_filter 等参数可以调整这一行为

背景,内网一台虚拟机用 wireguard 和外部一个 CN2 出口打了隧道,希望将这台虚拟机配置为内网 VPN 网关,其他机器将任意 IP 的路由指向此虚拟机(以下简称 gw)即可实现走 CN2 访问目的 IP。

在实际操作中,发现一个现象,按照上述流程配置过 iptables 和路由规则后,有时仍会发现内网的包不会按照预期转发到 gw 的 wghub 接口,此时在 gw 抓包会发现内网其他主机发来的包不是从 ethA 进入,查看内网其他主机的 ARP ,发现 ARP 缓存是错误的,ip neigh flush all 后使用 arping 进行测试,发现无论请求 192.168.0.a 还是 192.168.0.b,gw 都会同时将两张网卡的 MAC 地址作为响应,且响应返回时先后顺序固定,因此不明就里的客户端就会取第一个响应记录到 ARP 表。

TL; DR:本文主要从 socket 的关闭方式谈到 socket 的 SO_LINGER 属性,进一步分析 nginx 关于 lingering 的配置和源码,浅谈如何避免 socket 关闭时可能出现的一些问题。

在 Unix 上,一个连接实际上对应的是内核里的一个 fd, sockfd = socket(int socket_family, int socket_type, int protocol) ,因此 socket 的关闭实际上是使用 close() 调用将其当作一个普通 fd 进行关闭;但是对于 TCP 连接,可以使用 shutdown() 系统调用,将其当作一个全双工连接进行关闭后再使用 close() 进行销毁,两种方式在行为上略有区别。

nf_conntrack 的数据结构和相关参数调优

在内核中,连接跟踪表是一个二维数组结构的哈希表(hash table),哈希表的大小记作HASHSIZE,哈希表的每一项(hash table entry)称作bucket,因此哈希表中有HASHSIZE个bucket存在,每个bucket包含一个链表(linked list),每个链表能够存放若干个conntrack条目(bucket size)。需要明确的是,nf_conntrack 模块并不是所有 Linux 内核都会加载,最常见的导致加载该模块的原因是使用了 iptables、lvs 等内核态 NAT/防火墙导致内核需要对连接表进行追踪,iptable_nat、ip_vs 等多个内核模块都依赖 nf_conntrack, 但是 nf_conntrack 的存在会影响高并发下的内核收包性能。