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

feat: udp conntrack for lan interface #699

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 (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/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