Skip to content

Commit

Permalink
feat: apply udp conntrack for lan ingress
Browse files Browse the repository at this point in the history
  • Loading branch information
LostAttractor committed Nov 26, 2024
1 parent 81088e2 commit 681fe50
Showing 1 changed file with 96 additions and 72 deletions.
168 changes: 96 additions & 72 deletions control/kern/tproxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
{
Expand All @@ -1339,19 +1360,22 @@ int tproxy_wan_ingress(struct __sk_buff *skb)
return TC_ACT_OK;
int ret = parse_transport(skb, link_h_len, &ethh, &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;
}
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 681fe50

Please sign in to comment.