为了降本增效,网关部署到了业务的节点上,参考 eBPF Talk: 善用 TCP option 来支持网关 ping。然而,业务节点上可能会配置不少 iptables 规则,从该节点 ping 另一台网关的时候,可能会被 iptables 规则丢包。

IPTABLES DROP

为了解决这个问题,可以使用隔离的 netns 来避免 iptables 规则干扰 ping 包的收发。

准备隔离的 netns

在隔离的 netns 里,怎么收发 ping 包呢?

  1. 通过 veth pair 连接隔离的 netns 和 host netns;
  2. 在隔离的 netns 里,配置 MAC 地址;
  3. 在隔离的 netns 里,配置 IP 地址;
  4. 在隔离的 netns 里,配置默认路由;
  5. 在隔离的 netns 里,配置网关的 ARP 表项;
  6. 在 host netns 里,使用 XDP 将从隔离的 netns 发出的 ping 包转发到真实的网关;
  7. 在 host netns 里,使用 XDP 将接收到的 ping 包转发到隔离的 netns;
  8. 在隔离的 netns 里,开启 veth GRO 用来接收 ping 包。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NETNS="ns-ping"
VETH0="vpingh"
VETH1="vpingn"

IP="${1}"
MAC="${2}"
GW_IP="${3}"
GW_MAC="${4}"

ip netns add ${NETNS}

ip link add ${VETH0} type veth peer name ${VETH1}
ip link set ${VETH1} netns ${NETNS}
ip link set ${VETH0} up

ip netns exec ${NETNS} bash -c "
ip link set ${VETH1} address ${MAC}
ip link set ${VETH1} up
ip addr add ${IP}/24 dev ${VETH1}
ip route add default via ${GW_IP} dev ${VETH1}
ip neigh add ${GW_IP} dev ${VETH1} lladdr ${GW_MAC} nud permanent
ethtool -K ${VETH1} gro on
"

使用 XDP 转发 ping 包的代码:

RXTX PING

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#define PING_TCP_DPORT 10086

static const volatile u32 ETH_IFINDEX = 0;
static const volatile u32 VETH_IFINDEX = 0;

SEC("xdp")
int tx_ping(struct xdp_md *xdp)
{
    return bpf_redirect(ETH_IFINDEX, 0);
}

SEC("xdp")
int rx_ping(struct xdp_md *xdp)
{
    struct ethhdr *eth = ctx_ptr(xdp, data);
    struct iphdr *iph = eth + 1;
    struct tcphdr *tcph;

    if ((void *) (iph + 1) > ctx_ptr(xdp, data_end))
        return XDP_PASS;

    if (iph->protocol != IPPROTO_TCP)
        return XDP_PASS;

    tcph = (void *) iph + iph->ihl << 2;

    if ((void *) (tcph + 1) > ctx_ptr(xdp, data_end))
        return XDP_PASS;

    if (tcph->source != bpf_htons(PING_TCP_DPORT))
        return XDP_PASS;

    return bpf_redirect(VETH_IFINDEX, 0);
}

其中,tx_ping() 用来将 ping 包转发到物理网口,rx_ping() 用来将 ping 包转发到隔离的 netns。

最终,便能复用原有的基于 rawsocket 的收发 ping 包的代码逻辑了;只是在准备 rawsocket 的时候,需要进入到隔离的 netns 里。

小结

通过使用隔离的 netns,可以避免 iptables 规则干扰 ping 包的收发,从而更好地支持网关跟业务节点混部的场景。毕竟,还得继续降本增效嘛。