eBPF Talk: 善用 TCP option 來支持網關 ping

這裏的網關 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[1] 符合需求,就它了。

使用 gopacket[2] 發包時,將 MAGIC 寫入該 TCP option 即可:

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 等之後,就需要做如下處理:

#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 包。

以此,更好地支持網關跟業務節點混部的場景。畢竟,得降本增效嘛。

參考資料

[1]

TCP timestamps option: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_timestamps

[2]

gopacket: https://pkg.go.dev/github.com/google/gopacket@v1.1.19/layers#TCP

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/BKo2lAOoh_6BAsKoC5SjdA