Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Experimental] Stack bypass #7

Open
wants to merge 28 commits into
base: main
Choose a base branch
from

Conversation

jschwinger233
Copy link
Owner

@jschwinger233 jschwinger233 commented Feb 14, 2024

Background

劫持路径的 stack bypass 实现。

之前的问题集中在:

  1. netfilter:如 wan ingress 入栈后需要额外配置 nft 允许 0x8000000、lan ingress 和 nft flow table 冲突、等。
  2. sysctl:如 wan ingress 需要设置 sysctl accept_local=1、dae0 需要设置 rp_filter=0、等。
  3. 二层邻居系统:如 dae0 的 lladdr 被 systemd 修改。

这个 PR 试图把“劫持路径”(“分流路径”)绕过内核栈,并且保持 datapath 对称,希望能解决大部分问题。

Datapath

0.5 wan: 注意劫持路径请求和回复是非对称的,而且从 wan0 到 dae 的网络栈造成了大部分问题


            bpf_redirect
            bpf_sk_assign
               ┌────┐
         ┌─────►wan0├─────┐
         │     └────┘     │
 request │                │request
         │                │
       ┌─┴──┐  reply    ┌─▼──┐
       │curl◄───────────┤dae │
       └────┘           └────┘

0.5 lan for udp: 请求和回复也是非对称的,而且 dae0 的 lladdr + sysctl 可能被 systemd 修改也造成了不少问题


  bpf_sk_assign
     ┌────┐  request  ┌───┐
     │lan0├───────────►dae│
     └─▲──┘           └─┬─┘
       │                │reply
 reply │       ┌────────┼───┐
       │       │        │   │
       │  ┌────┼────┐   │   │
       └──┤dae0│peer◄───┘   │
          └────┼────┘       │
               │   dae netns│
               └────────────┘

新的 wan datapath:注意 wan0 和 dae0 之间在双向都是通过 bpf_redirect 跳过内核栈,所以不需要配置 nft 和 sysctl。

                            ┌──────────────────┐
                            │                  │
 ┌────┐     ┌────┐     ┌────┼────┐      ┌───┐  │
 │    ├─────►    ├─────►    │    ├──────►   │  │
 │curl│     │wan0│     │dae0│peer│      │dae│  │
 │    ◄─────┤    ◄─────┤    │    ◄──────┤   │  │
 └────┘     └────┘     └────┼────┘      └───┘  │
                            │     dae netns    │
                            └──────────────────┘

新 lan datapath: lan0 和 dae0 之间也是 bpf_redirect

                 ┌──────────────────┐
                 │                  │
 ┌────┐     ┌────┼────┐      ┌───┐  │
 │    ├─────►    │    ├──────►   │  │
 │lan0│     │dae0│peer│      │dae│  │
 │    ◄─────┤    │    ◄──────┤   │  │
 └────┘     └────┼────┘      └───┘  │
                 │     dae netns    │
                 └──────────────────┘

新路径是完全对称路径,希望能尽量减少潜在的问题。

Implementation

  1. bpf prog

需要四个 tc bpf prog:

    1. lan_ingress: 调用 route() 做分流决策,对于分流流量调用 bpf_redirect 去 dae0。redirect 之前要记录一个 redirect_track,key 是 (sip, dip, l4proto),value 是 (smac, dmac, ifindex)。redirect 之前还要修改 ethhdr->dest 为 dae0-peer 的 lladdr
  • lan_egress: 不需要
    1. wan_egress: 调用 route() 做分流决策,对于分流流量调用 bpf_redirect 去 dae0。redirect 之前要记录一个 redirect_track,key 是 (sip, dip, l4proto),value 是 (smac, dmac, ifindex)。redirect 之前还要修改 ethhdr->dest 为 dae0-peer 的 lladdr
  • wan_ingress:不需要
  • dae0_egress:不需要
    1. dae0_ingress:处理 dae 进程的回复流量。查询 redirect_track,修改二层头,调用 bpf_redirect 重定向给 wan0 或 lan0。
    1. dae0peer_ingress:处理分流请求流量,调用 sk_lookup + sk_assign。
  1. control

a. 只在 dae netns 里监听 :12345
b. 不需要监听 dae0 lladdr,现在只需要 dae0-peer 的 lladdr,但它在 dae netns 里面,应该不会被修改(应该吧。。。)
c. 删除 autoConfigFirewall flag,因为不需要配置 nft
d. ip rule 只需要在 dae netns 里设置

Code Walkthrough

  • kernel-test.yaml: bump action version,新增了几个测试
  • cmd/run.go: 把 c.ListenAndServe() 放在 DaeNetns 里运行
  • config/config.go: 删除 AutoConfigFirewallRule
  • bpf_utils.go: 需要新注入几个常量给 bpf prog
  • control_plane.go: 把 DaeNetns.Setup() 提前运行,删除 nft AcceptInputMark, setupRoutingPolicy 放到 DaeNetns 里运行,新增一个 bindDaens 函数调用
  • control_plane_core.go: 主要是实现 bindDaens 函数,这个函数里把 dae0_ingress 和 dae0peer_ingress 两个 bpf attach 上去
  • tproxy.c: 略
  • netns_utils.go: 减少 setupSysctl 的设置,删除 monitorDae0LinkAddr
  • udp.go: 不需要检测 端口冲突,直接在 dae netns 里回复 udp

Checklist

Full Changelogs

  • [Implement ...]

Issue Reference

Closes #[issue number]

Test Result

@jschwinger233 jschwinger233 force-pushed the gray/exp/wan-redirect-to-dae0 branch 3 times, most recently from deae952 to 2ba0dbb Compare February 18, 2024 17:38
@jschwinger233 jschwinger233 changed the title [Experimental] redirect to dae0 [Experimental] Stack bypass Feb 19, 2024
Avoid spammy dmesg reported by @umlka:

[   16.726876] dae0peer: Caught tx_queue_len zero misconfig
[   16.786837] dae0: Caught tx_queue_len zero misconfig
skb->mark will be reset when going across netns (skb_scrub_packet), so
this commit sets a special value in cb[0] which can survive bpf_redirect
and netns crossing.

This solves issues like:

level=warning msg="No AddrPort presented: reading map: key [[::ffff:0.0.0.0]:68, 17, 255.255.255.255:67]: lookup: key does not exist"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants