From 99fc361db3cdaecb610f713b9a2501796bdfbec3 Mon Sep 17 00:00:00 2001 From: ChaosAttractor Date: Fri, 20 Sep 2024 02:07:40 +0800 Subject: [PATCH 1/2] feat: apply udp conntrack for lan ingress --- control/kern/tproxy.c | 168 ++++++++++++++++++++++++------------------ 1 file changed, 96 insertions(+), 72 deletions(-) diff --git a/control/kern/tproxy.c b/control/kern/tproxy.c index 2eb64710e4..228ffe1541 100644 --- a/control/kern/tproxy.c +++ b/control/kern/tproxy.c @@ -363,7 +363,7 @@ struct { } cookie_pid_map SEC(".maps"); struct udp_conn_state { - bool is_egress; + bool is_inbound_flow; // For each flow (echo symmetric path), note the original flow direction struct bpf_timer timer; }; @@ -994,12 +994,64 @@ static __always_inline void prep_redirect_to_control_plane( skb->cb[1] = l4proto; } +static __always_inline void copy_reversed_tuples(struct tuples_key *key, + struct tuples_key *dst) +{ + __builtin_memset(dst, 0, sizeof(*dst)); + dst->dip = key->sip; + dst->sip = key->dip; + dst->sport = key->dport; + dst->dport = key->sport; + dst->l4proto = key->l4proto; +} + +static int refresh_udp_conn_state_timer_cb(void *_udp_conn_state_map, + struct tuples_key *key, + struct udp_conn_state *val) +{ + bpf_map_delete_elem(&udp_conn_state_map, key); + return 0; +} + +static __always_inline struct udp_conn_state * +refresh_udp_conn_state_timer(struct tuples_key *key, bool is_inbound_flow) +{ + struct udp_conn_state *old_conn_state = + bpf_map_lookup_elem(&udp_conn_state_map, key); + struct udp_conn_state new_conn_state = { 0 }; + + if (old_conn_state) + new_conn_state.is_inbound_flow = + old_conn_state->is_inbound_flow; // Keep the original value if flow exist. + else + new_conn_state.is_inbound_flow = is_inbound_flow; + long ret = bpf_map_update_elem(&udp_conn_state_map, key, + &new_conn_state, BPF_ANY); + if (unlikely(ret)) + return NULL; + struct udp_conn_state *value = + bpf_map_lookup_elem(&udp_conn_state_map, key); + if (unlikely(!value)) + return NULL; + + if ((ret = bpf_timer_init(&value->timer, &udp_conn_state_map, + CLOCK_MONOTONIC))) + goto retn; + + if ((ret = bpf_timer_set_callback(&value->timer, + refresh_udp_conn_state_timer_cb))) + goto retn; + + if ((ret = bpf_timer_start(&value->timer, TIMEOUT_UDP_CONN_STATE, 0))) + goto retn; + +retn: + return value; +} + SEC("tc/egress") int tproxy_lan_egress(struct __sk_buff *skb) { - if (skb->ingress_ifindex != NOWHERE_IFINDEX) - return TC_ACT_PIPE; - struct ethhdr ethh; struct iphdr iph; struct ipv6hdr ipv6h; @@ -1018,10 +1070,25 @@ int tproxy_lan_egress(struct __sk_buff *skb) bpf_printk("parse_transport: %d", ret); return TC_ACT_OK; } - if (l4proto == IPPROTO_ICMPV6 && icmp6h.icmp6_type == NDP_REDIRECT) { + + if (skb->ingress_ifindex == NOWHERE_IFINDEX && // Only handle packets from local + l4proto == IPPROTO_ICMPV6 && icmp6h.icmp6_type == NDP_REDIRECT) { // REDIRECT (NDP) return TC_ACT_SHOT; } + + // Update UDP Conntrack + if (l4proto == IPPROTO_UDP) { + struct tuples tuples; + struct tuples_key reversed_tuples_key; + + get_tuples(skb, &tuples, &iph, &ipv6h, &tcph, &udph, l4proto); + copy_reversed_tuples(&tuples.five, &reversed_tuples_key); + + if (!refresh_udp_conn_state_timer(&reversed_tuples_key, true)) + return TC_ACT_SHOT; + } + return TC_ACT_PIPE; } @@ -1116,6 +1183,15 @@ new_connection:; params.l4hdr = &tcph; params.flag[0] = L4ProtoType_TCP; } else { + struct udp_conn_state *conn_state = + refresh_udp_conn_state_timer(&tuples.five, false); + if (!conn_state) + return TC_ACT_SHOT; + if (conn_state->is_inbound_flow) { + // Replay (outbound) of an inbound flow + // => direct. + return TC_ACT_OK; + } params.l4hdr = &udph; params.flag[0] = L4ProtoType_UDP; } @@ -1267,61 +1343,6 @@ static __always_inline bool pid_is_control_plane(struct __sk_buff *skb, return false; } -static int refresh_udp_conn_state_timer_cb(void *_udp_conn_state_map, - struct tuples_key *key, - struct udp_conn_state *val) -{ - bpf_map_delete_elem(&udp_conn_state_map, key); - return 0; -} - -static __always_inline void copy_reversed_tuples(struct tuples_key *key, - struct tuples_key *dst) -{ - __builtin_memset(dst, 0, sizeof(*dst)); - dst->dip = key->sip; - dst->sip = key->dip; - dst->sport = key->dport; - dst->dport = key->sport; - dst->l4proto = key->l4proto; -} - -static __always_inline struct udp_conn_state * -refresh_udp_conn_state_timer(struct tuples_key *key, bool is_egress) -{ - struct udp_conn_state *old_conn_state = - bpf_map_lookup_elem(&udp_conn_state_map, key); - struct udp_conn_state new_conn_state = { 0 }; - - if (old_conn_state) - new_conn_state.is_egress = - old_conn_state->is_egress; // Keep the value. - else - new_conn_state.is_egress = is_egress; - long ret = bpf_map_update_elem(&udp_conn_state_map, key, - &new_conn_state, BPF_ANY); - if (unlikely(ret)) - return NULL; - struct udp_conn_state *value = - bpf_map_lookup_elem(&udp_conn_state_map, key); - if (unlikely(!value)) - return NULL; - - if ((bpf_timer_init(&value->timer, &udp_conn_state_map, - CLOCK_MONOTONIC))) - goto retn; - - if ((bpf_timer_set_callback(&value->timer, - refresh_udp_conn_state_timer_cb))) - goto retn; - - if ((bpf_timer_start(&value->timer, TIMEOUT_UDP_CONN_STATE, 0))) - goto retn; - -retn: - return value; -} - SEC("tc/wan_ingress") int tproxy_wan_ingress(struct __sk_buff *skb) { @@ -1339,19 +1360,22 @@ int tproxy_wan_ingress(struct __sk_buff *skb) return TC_ACT_OK; int ret = parse_transport(skb, link_h_len, ðh, &iph, &ipv6h, &icmp6h, &tcph, &udph, &ihl, &l4proto); - if (ret) + if (ret) { + bpf_printk("parse_transport: %d", ret); return TC_ACT_OK; - if (l4proto != IPPROTO_UDP) - return TC_ACT_PIPE; + } - struct tuples tuples; - struct tuples_key reversed_tuples_key; + // Update UDP Conntrack + if (l4proto == IPPROTO_UDP) { + struct tuples tuples; + struct tuples_key reversed_tuples_key; - get_tuples(skb, &tuples, &iph, &ipv6h, &tcph, &udph, l4proto); - copy_reversed_tuples(&tuples.five, &reversed_tuples_key); + get_tuples(skb, &tuples, &iph, &ipv6h, &tcph, &udph, l4proto); + copy_reversed_tuples(&tuples.five, &reversed_tuples_key); - if (!refresh_udp_conn_state_timer(&reversed_tuples_key, false)) - return TC_ACT_SHOT; + if (!refresh_udp_conn_state_timer(&reversed_tuples_key, true)) + return TC_ACT_SHOT; + } return TC_ACT_PIPE; } @@ -1547,11 +1571,11 @@ int tproxy_wan_egress(struct __sk_buff *skb) } struct udp_conn_state *conn_state = - refresh_udp_conn_state_timer(&tuples.five, true); + refresh_udp_conn_state_timer(&tuples.five, false); if (!conn_state) return TC_ACT_SHOT; - if (!conn_state->is_egress) { - // Input udp connection + if (conn_state->is_inbound_flow) { + // Replay (outbound) of an inbound flow // => direct. return TC_ACT_OK; } From 3f636187db705837e30eaa7e57aa07d4e675a486 Mon Sep 17 00:00:00 2001 From: ChaosAttractor Date: Fri, 20 Sep 2024 02:21:13 +0800 Subject: [PATCH 2/2] chore: clean up code --- control/kern/tproxy.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/control/kern/tproxy.c b/control/kern/tproxy.c index 228ffe1541..6145801103 100644 --- a/control/kern/tproxy.c +++ b/control/kern/tproxy.c @@ -1034,15 +1034,15 @@ refresh_udp_conn_state_timer(struct tuples_key *key, bool is_inbound_flow) if (unlikely(!value)) return NULL; - if ((ret = bpf_timer_init(&value->timer, &udp_conn_state_map, - CLOCK_MONOTONIC))) + if (bpf_timer_init(&value->timer, &udp_conn_state_map, + CLOCK_MONOTONIC)) goto retn; - if ((ret = bpf_timer_set_callback(&value->timer, - refresh_udp_conn_state_timer_cb))) + if (bpf_timer_set_callback(&value->timer, + refresh_udp_conn_state_timer_cb)) goto retn; - if ((ret = bpf_timer_start(&value->timer, TIMEOUT_UDP_CONN_STATE, 0))) + if (bpf_timer_start(&value->timer, TIMEOUT_UDP_CONN_STATE, 0)) goto retn; retn: