这里的网关 ping 指的是对虚拟网络网关发包,确认网关的数据面能够符合预期地转发网络包。

在部署、升级网关的时候,需要对网关进行 ping 测试,以确认网关的数据面符合预期地转发网络包。目前,发的 ping 包是 tcp SYN 包。

然而,存在一个网关跟业务节点混部的场景,导致网关的数据面不知道该将 SYN 包转发出去,还是放行到内核协议栈。

近来,研究了一下解决办法:

  1. 将 MAGIC 存放在 SYN 包的 payload 中;
  2. 将 MAGIC 存放在 TCP option 中;

网关判断 magic number 是不是网关 ping 包:

  1. 如果是 MAGIC,则将 MAGIC 减一,然后转发出去;
  2. 如果是 MAGIC-1,则放行到内核协议栈。

经过 POC 验证,方法 1 不可行;因为取决于内核协议栈的行为,可能直接丢包,也有可能回包。

经过 POC 验证,方法 2 可行。

选择 TCP option

因为需要存放 MAGIC,所以需要选择能支持值为 4 个字节长度的 option。

快速了解了一下 TCP options,看到 TCP timestamps option 符合需求,就它了。

使用 gopacket 发包时,将 MAGIC 写入该 TCP option 即可:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const MAGIC = 0xdeadbeef

optData := make([]byte, 8)
binary.BigEndian.PutUint32(optData[:4], MAGIC)

tcpLayer := layers.TCP{
    // ...
    Options: []layers.TCPOption{
        {OptionType: layers.TCPOptionKindTimestamps, OptionLength: 10, OptionData: optData},
    },
}

网关对该 TCP option 的支持

因为是 tcp SYN 包,所以在判断协议、SYN 等之后,就需要做如下处理:

 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 MAGIC       0xdeadbeef
#define MAGIC_PASS  0xddadbeef

struct tcp_option_timestamps {
    u8 type;
    u8 len;
    u32 magic;
    u32 pad;
} __packed;

static __always_inline bool
pass_to_kernel(struct iphdr *iph) {
    // ...

    struct tcphdr *tcph = (typeof(tcph)) ((void *) iph + (iph->ihl << 2));
    // ...

    struct tcp_option_timestamps *topt = (typeof(topt)) (tcph + 1);

    if (topt->magic == MAGIC_PASS)
        return true;

    if (topt->magic != MAGIC)
        return false;

    // Update tcp checksum.
    u32 check = (u32) tcph->check;
    check += (u32) bpf_htons(0x0100);
    tcp->check = (u16) (check + (check>=0xFFFF));

    topt->magic--;

    return false;
}
  1. MAGIC_PASS 是 MAGIC 以大端存放在 option 里而后在小端服务器上减一的结果。
  2. 判断 magic 是 MAGIC_PASS,则放行到内核协议栈。
  3. 判断 magic 不是 MAGIC,则跳过对 magic 的处理。
  4. 参考 IP 头部 TTL 减一时的校验和更新方法,更新 TCP 头部的校验和。
  5. magic 减一。

如此,网关便可判断出一个 ping 包该放行到内核协议栈还是该转发走了。

小结

通过将 MAGIC 保存到 TCP timestamps option 的方式,巧妙地让网关正确地判断出 ping 包、并正确地处理 ping 包。

以此,更好地支持网关跟业务节点混部的场景。毕竟,得降本增效嘛。