From 438c05cbd64b5c41c4a47a00e82451f03ee4320b Mon Sep 17 00:00:00 2001 From: "./gray" Date: Mon, 14 Oct 2024 11:21:45 +0800 Subject: [PATCH] ci(workflows/bpf-test): add BPF test to cover route logic (#671) Co-authored-by: mzz <2017@duck.com> --- .github/workflows/bpf-test.yml | 28 + Makefile | 16 + control/kern/tests/bpf_test.c | 1006 ++++++++++++++++++++++++++++++++ control/kern/tests/bpf_test.go | 167 ++++++ control/kern/tests/bpf_test.h | 184 ++++++ 5 files changed, 1401 insertions(+) create mode 100644 .github/workflows/bpf-test.yml create mode 100644 control/kern/tests/bpf_test.c create mode 100644 control/kern/tests/bpf_test.go create mode 100644 control/kern/tests/bpf_test.h diff --git a/.github/workflows/bpf-test.yml b/.github/workflows/bpf-test.yml new file mode 100644 index 0000000..09c5e3b --- /dev/null +++ b/.github/workflows/bpf-test.yml @@ -0,0 +1,28 @@ +name: BPF Test + +on: + pull_request: + paths: + - "**/*.c" + - "**/*.h" + - "go.mod" + - "go.sum" + - ".github/workflows/bpf-test.yml" + +permissions: read-all + +jobs: + bpf_tests: + name: BPF Unit Test + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + with: + persist-credentials: false + fetch-depth: 0 + - name: Run BPF tests + run: | + git submodule update --init + sudo make ebpf-test || (echo "Run 'make ebpf-test' locally to investigate failures"; exit 1) + diff --git a/Makefile b/Makefile index 4e6afef..354f95a 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,8 @@ clean-ebpf: rm -f control/bpf_bpf*.o @rm -f trace/bpf_bpf*.go && \ rm -f trace/bpf_bpf*.o + @rm -f control/kern/tests/bpftest_bpf*.go && \ + rm -f control/kern/tests/bpftest_bpf*.o fmt: go fmt ./... @@ -99,4 +101,18 @@ ebpf: submodule clean-ebpf ebpf-lint: ./scripts/checkpatch.pl --no-tree --strict --no-summary --show-types --color=always control/kern/tproxy.c --ignore COMMIT_COMMENT_SYMBOL,NOT_UNIFIED_DIFF,COMMIT_LOG_LONG_LINE,LONG_LINE_COMMENT,VOLATILE,ASSIGN_IN_IF,PREFER_DEFINED_ATTRIBUTE_MACRO,CAMELCASE,LEADING_SPACE,OPEN_ENDED_LINE,SPACING,BLOCK_COMMENT_STYLE +ebpf-test: export BPF_CLANG := $(CLANG) +ebpf-test: export BPF_STRIP_FLAG := $(STRIP_FLAG) +ebpf-test: export BPF_CFLAGS := $(CFLAGS) +ebpf-test: export BPF_TARGET := $(TARGET) +ebpf-test: export BPF_TRACE_TARGET := $(GOARCH) +ebpf-test: submodule clean-ebpf + @unset GOOS && \ + unset GOARCH && \ + unset GOARM && \ + echo $(STRIP_FLAG) && \ + go generate ./control/kern/tests/bpf_test.go && \ + go clean -testcache && \ + go test -v ./control/kern/tests/... + ## End Ebpf diff --git a/control/kern/tests/bpf_test.c b/control/kern/tests/bpf_test.c new file mode 100644 index 0000000..b251a01 --- /dev/null +++ b/control/kern/tests/bpf_test.c @@ -0,0 +1,1006 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Copyright (c) 2022-2024, daeuniverse Organization + +//go:build exclude + +#define __DEBUG +#define __DEBUG_ROUTING +#define __PRINT_ROUTING_RESULT + +#include "../tproxy.c" +#include "./bpf_test.h" + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(key_size, sizeof(__u32)); + __uint(max_entries, 1); + __array(values, int()); +} entry_call_map SEC(".maps") = { + .values = { + [0] = &tproxy_wan_egress, + }, +}; + +SEC("tc/pktgen/dport_match") +int testpktgen_dport_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 80); +} + +SEC("tc/setup/dport_match") +int testsetup_dport_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* dport(80) -> proxy */ + struct match_set ms = {}; + struct port_range pr = {80, 80}; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/dport_match") +int testcheck_dport_match(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 80); +} + +SEC("tc/pktgen/dport_mismatch") +int testpktgen_dport_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/dport_mismatch") +int testsetup_dport_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* dport(80) -> proxy */ + struct match_set ms = {}; + struct port_range pr = {80, 80}; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/dport_mismatch") +int testcheck_dport_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/ipset_match") +int testpktgen_ipset_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(224,1,0,2), 19233, 80); +} + +SEC("tc/setup/ipset_match") +int testsetup_ipset_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* dip(224.1.0.0/16) -> direct */ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_IpSet; + ms.outbound = 0; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 112; // */16 + lpm_key.data[2] = bpf_ntohl(0xffff); + lpm_key.data[3] = bpf_ntohl(0xe0010000); // 224.1.0.0 + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + /* fallback: proxy */ + set_routing_fallback(OUTBOUND_USER_DEFINED_MIN, false); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/ipset_match") +int testcheck_ipset_match(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(224,1,0,2), + 19233, 80); +} + +SEC("tc/pktgen/ipset_mismatch") +int testpktgen_ipset_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(225,1,0,2), 19233, 80); +} + +SEC("tc/setup/ipset_mismatch") +int testsetup_ipset_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + // dip(224.1.0.0/16) -> direct + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_IpSet; + ms.outbound = 0; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 112; // */16 + lpm_key.data[2] = bpf_ntohl(0xffff); + lpm_key.data[3] = bpf_ntohl(0xe0010000); // 224.1.0.0 + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + /* fallback: proxy */ + set_routing_fallback(OUTBOUND_USER_DEFINED_MIN, false); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/ipset_mismatch") +int testcheck_ipset_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(225,1,0,2), + 19233, 80); +} + +SEC("tc/pktgen/source_ipset_match") +int testpktgen_source_ipset_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,50,1), IPV4(224,1,0,2), 19233, 80); +} + +SEC("tc/setup/source_ipset_match") +int testsetup_source_ipset_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* sip(192.168.50.0/24) -> direct */ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_SourceIpSet; + ms.outbound = 0; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 120; + lpm_key.data[2] = bpf_ntohl(0xffff); + lpm_key.data[3] = bpf_ntohl(0xc0a83200); // 192.168.50.0 + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + /* fallback: proxy */ + set_routing_fallback(OUTBOUND_USER_DEFINED_MIN, false); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/source_ipset_match") +int testcheck_source_ipset_match(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,50,1), IPV4(224,1,0,2), + 19233, 80); +} + +SEC("tc/pktgen/source_ipset_mismatch") +int testpktgen_source_ipset_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,51,1), IPV4(224,1,0,2), 19233, 80); +} + +SEC("tc/setup/source_ipset_mismatch") +int testsetup_source_ipset_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* sip(192.168.50.0/24) -> direct */ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_SourceIpSet; + ms.outbound = 0; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 120; + lpm_key.data[2] = bpf_ntohl(0xffff); + lpm_key.data[3] = bpf_ntohl(0xc0a83200); // 192.168.50.0 + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + /* fallback: proxy */ + set_routing_fallback(OUTBOUND_USER_DEFINED_MIN, false); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/source_ipset_mismatch") +int testcheck_source_ipset_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,51,1), IPV4(224,1,0,2), + 19233, 80); +} + +SEC("tc/pktgen/sport_match") +int testpktgen_sport_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 80); +} + +SEC("tc/setup/sport_match") +int testsetup_sport_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* sport(19000-20000) -> proxy */ + struct match_set ms = {}; + struct port_range pr = {19000, 20000}; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_SourcePort; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/sport_match") +int testcheck_sport_match(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 80); +} + +SEC("tc/pktgen/sport_mismatch") +int testpktgen_sport_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/sport_mismatch") +int testsetup_sport_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* sport(19230-19232) -> proxy */ + struct match_set ms = {}; + struct port_range pr = {19230, 19232}; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_SourcePort; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/sport_mismatch") +int testcheck_sport_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/l4proto_match") +int testpktgen_l4proto_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/l4proto_match") +int testsetup_l4proto_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* l4proto(tcp) -> proxy */ + struct match_set ms = {}; + ms.l4proto_type = L4ProtoType_TCP; + ms.not = false; + ms.type = MatchType_L4Proto; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/l4proto_match") +int testcheck_l4proto_match(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/l4proto_mismatch") +int testpktgen_l4proto_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/l4proto_mismatch") +int testsetup_l4proto_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* l4proto(udp) -> proxy */ + struct match_set ms = {}; + ms.l4proto_type = L4ProtoType_UDP; + ms.not = false; + ms.type = MatchType_L4Proto; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/l4proto_mismatch") +int testcheck_l4proto_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/ipversion_match") +int testpktgen_ipversion_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/ipversion_match") +int testsetup_ipversion_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* ipversion(4) -> proxy */ + struct match_set ms = {}; + ms.ip_version = IpVersionType_4; + ms.not = false; + ms.type = MatchType_IpVersion; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/ipversion_match") +int testcheck_ipversion_match(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/ipversion_mismatch") +int testpktgen_ipversion_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/ipversion_mismatch") +int testsetup_ipversion_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* ipversion(6) -> proxy */ + struct match_set ms = {}; + ms.ip_version = IpVersionType_6; + ms.not = false; + ms.type = MatchType_IpVersion; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/ipversion_mismatch") +int testcheck_ipversion_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/mac_match") +int testpktgen_mac_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/mac_match") +int testsetup_mac_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* mac('06:07:08:09:0a:0b') -> proxy */ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_Mac; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 128; + __u8 *data = (__u8 *)&lpm_key.data; + data[10] = 0x6; + data[11] = 0x7; + data[12] = 0x8; + data[13] = 0x9; + data[14] = 0xa; + data[15] = 0xb; + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/mac_match") +int testcheck_mac_match(struct __sk_buff *skb) +{ + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 128; + __u8 *data = (__u8 *)&lpm_key.data; + data[10] = 0x6; + data[11] = 0x7; + data[12] = 0x8; + data[13] = 0x9; + data[14] = 0xa; + data[15] = 0xb; + bpf_map_delete_elem(&unused_lpm_type, &lpm_key); + + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/mac_mismatch") +int testpktgen_mac_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/mac_mismatch") +int testsetup_mac_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* mac('00:01:02:03:04:05') -> proxy */ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_Mac; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 128; + __u8 *data = (__u8 *)&lpm_key.data; + data[10] = 0x0; + data[11] = 0x1; + data[12] = 0x2; + data[13] = 0x3; + data[14] = 0x4; + data[15] = 0x5; + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/mac_mismatch") +int testcheck_mac_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/dscp_match") +int testpktgen_dscp_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/dscp_match") +int testsetup_dscp_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* dscp(4) -> proxy */ + struct match_set ms = {}; + ms.dscp = 4; + ms.not = false; + ms.type = MatchType_Dscp; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/dscp_match") +int testcheck_dscp_match(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/dscp_mismatch") +int testpktgen_dscp_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/dscp_mismatch") +int testsetup_dscp_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* dscp(5) -> proxy */ + struct match_set ms = {}; + ms.dscp = 5; + ms.not = false; + ms.type = MatchType_Dscp; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/dscp_mismatch") +int testcheck_dscp_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/and_match_1") +int testpktgen_and_match_1(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/and_match_1") +int testsetup_and_match_1(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* dip(1.1.0.0/16) && l4proto(tcp) && dport(1-1023, 8443) -> proxy */ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_IpSet; + ms.outbound = OUTBOUND_LOGICAL_AND; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 112; // */16 + lpm_key.data[2] = bpf_ntohl(0xffff); + lpm_key.data[3] = bpf_ntohl(0x01010000); // 1.1.0.0 + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + ms.l4proto_type = L4ProtoType_TCP; + ms.not = false; + ms.type = MatchType_L4Proto; + ms.outbound = OUTBOUND_LOGICAL_AND; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &one_key, &ms, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + struct port_range pr = {1, 1023}; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_LOGICAL_OR; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &two_key, &ms, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + pr.port_start = 8443; + pr.port_end = 8443; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &three_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + ms.not = false; + ms.type = MatchType_Fallback; + ms.outbound = OUTBOUND_DIRECT; + ms.must = true; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &four_key, &ms, BPF_ANY); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/and_match_1") +int testcheck_and_match_1(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} + +SEC("tc/pktgen/and_match_2") +int testpktgen_and_match_2(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 8443); +} + +SEC("tc/setup/and_match_2") +int testsetup_and_match_2(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* dip(1.1.0.0/16) && l4proto(tcp) && dport(1-1023, 8443) -> proxy */ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_IpSet; + ms.outbound = OUTBOUND_LOGICAL_AND; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 112; // */16 + lpm_key.data[2] = bpf_ntohl(0xffff); + lpm_key.data[3] = bpf_ntohl(0x01010000); // 1.1.0.0 + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + ms.l4proto_type = L4ProtoType_TCP; + ms.not = false; + ms.type = MatchType_L4Proto; + ms.outbound = OUTBOUND_LOGICAL_AND; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &one_key, &ms, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + struct port_range pr = {1, 1023}; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_LOGICAL_OR; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &two_key, &ms, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + pr.port_start = 8443; + pr.port_end = 8443; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &three_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + ms.not = false; + ms.type = MatchType_Fallback; + ms.outbound = OUTBOUND_DIRECT; + ms.must = true; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &four_key, &ms, BPF_ANY); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/and_match_2") +int testcheck_and_match_2(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 8443); +} + +SEC("tc/pktgen/and_mismatch") +int testpktgen_and_mismatch(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 2333); +} + +SEC("tc/setup/and_mismatch") +int testsetup_and_mismatch(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* dip(1.1.0.0/16) && l4proto(tcp) && dport(1-1023, 8443) -> proxy */ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_IpSet; + ms.outbound = OUTBOUND_LOGICAL_AND; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + struct lpm_key lpm_key = {}; + lpm_key.trie_key.prefixlen = 112; // */16 + lpm_key.data[2] = bpf_ntohl(0xffff); + lpm_key.data[3] = bpf_ntohl(0x01010000); // 1.1.0.0 + __u32 lpm_value = bpf_ntohl(0x01000000); + bpf_map_update_elem(&unused_lpm_type, &lpm_key, &lpm_value, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + ms.l4proto_type = L4ProtoType_TCP; + ms.not = false; + ms.type = MatchType_L4Proto; + ms.outbound = OUTBOUND_LOGICAL_AND; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &one_key, &ms, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + struct port_range pr = {1, 1023}; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_LOGICAL_OR; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &two_key, &ms, BPF_ANY); + + __builtin_memset(&ms, 0, sizeof(ms)); + pr.port_start = 8443; + pr.port_end = 8443; + ms.port_range = pr; + ms.not = false; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &three_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + ms.not = false; + ms.type = MatchType_Fallback; + ms.outbound = OUTBOUND_DIRECT; + ms.must = true; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &four_key, &ms, BPF_ANY); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/and_mismatch") +int testcheck_and_mismatch(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 2333); +} + +SEC("tc/pktgen/not_match") +int testpktgen_not_match(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 80); +} + +SEC("tc/setup/not_match") +int testsetup_not_match(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* !dport(80) -> proxy */ + struct match_set ms = {}; + struct port_range pr = {80, 80}; + ms.port_range = pr; + ms.not = true; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/not_match") +int testcheck_not_match(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_OK, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 80); +} + +SEC("tc/pktgen/not_mismtach") +int testpktgen_not_mismtach(struct __sk_buff *skb) +{ + return set_ipv4_tcp(skb, IPV4(192,168,0,1), IPV4(1,1,1,1), 19233, 79); +} + +SEC("tc/setup/not_mismtach") +int testsetup_not_mismtach(struct __sk_buff *skb) +{ + __u32 linklen = ETH_HLEN; + bpf_map_update_elem(&linklen_map, &one_key, &linklen, BPF_ANY); + + /* !dport(80) -> proxy */ + struct match_set ms = {}; + struct port_range pr = {80, 80}; + ms.port_range = pr; + ms.not = true; + ms.type = MatchType_Port; + ms.outbound = OUTBOUND_USER_DEFINED_MIN; + ms.must = false; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &zero_key, &ms, BPF_ANY); + + /* fallback: must_direct */ + set_routing_fallback(OUTBOUND_DIRECT, true); + + bpf_tail_call(skb, &entry_call_map, 0); + return TC_ACT_OK; +} + +SEC("tc/check/not_mismtach") +int testcheck_not_mismtach(struct __sk_buff *skb) +{ + return check_routing_ipv4_tcp(skb, + TC_ACT_REDIRECT, + IPV4(192,168,0,1), IPV4(1,1,1,1), + 19233, 79); +} diff --git a/control/kern/tests/bpf_test.go b/control/kern/tests/bpf_test.go new file mode 100644 index 0000000..b46c19c --- /dev/null +++ b/control/kern/tests/bpf_test.go @@ -0,0 +1,167 @@ +/* + * SPDX-License-Identifier: AGPL-3.0-only + * Copyright (c) 2022-2024, daeuniverse Organization + */ + +package tests + +import ( + "errors" + "fmt" + "os" + "reflect" + "strings" + "testing" + + "github.com/cilium/ebpf" + "github.com/vishvananda/netlink/nl" +) + +//go:generate go run -mod=mod github.com/cilium/ebpf/cmd/bpf2go -cc "$BPF_CLANG" "$BPF_STRIP_FLAG" -cflags "$BPF_CFLAGS" -target "$BPF_TARGET" bpftest ./bpf_test.c -- -I../headers -I. + +type programSet struct { + id string + pktgen *ebpf.Program + setup *ebpf.Program + check *ebpf.Program +} + +func runBpfProgram(prog *ebpf.Program, data, ctx []byte) (statusCode uint32, dataOut, ctxOut []byte, err error) { + dataOut = make([]byte, len(data)) + if len(dataOut) > 0 { + // See comments at https://github.com/cilium/ebpf/blob/20c4d8896bdde990ce6b80d59a4262aa3ccb891d/prog.go#L563-L567 + dataOut = make([]byte, len(data)+256+2) + } + ctxOut = make([]byte, len(ctx)) + opts := &ebpf.RunOptions{ + Data: data, + DataOut: dataOut, + Context: ctx, + ContextOut: ctxOut, + Repeat: 1, + } + ret, err := prog.Run(opts) + return ret, opts.DataOut, ctxOut, err +} + +func collectPrograms(t *testing.T) (progset []programSet, err error) { + obj := &bpftestObjects{} + pinPath := "/sys/fs/bpf/dae" + if err = os.MkdirAll(pinPath, 0755); err != nil && !os.IsExist(err) { + return + } + + if err = loadBpftestObjects(obj, + &ebpf.CollectionOptions{ + Maps: ebpf.MapOptions{ + PinPath: pinPath, + }, + Programs: ebpf.ProgramOptions{ + LogSize: ebpf.DefaultVerifierLogSize * 10, + }, + }, + ); err != nil { + var ( + ve *ebpf.VerifierError + verifierLog string + ) + if errors.As(err, &ve) { + verifierLog = fmt.Sprintf("Verifier error: %+v\n", ve) + } + + t.Fatalf("Failed to load objects: %s\n%+v", verifierLog, err) + + return nil, err + } + + if err = obj.LpmArrayMap.Update(uint32(0), obj.UnusedLpmType, ebpf.UpdateAny); err != nil { + t.Fatalf("Failed to update LpmArrayMap: %s", err) + return + } + + v := reflect.ValueOf(obj.bpftestPrograms) + typeOfV := v.Type() + for i := 0; i < v.NumField(); i++ { + progname := typeOfV.Field(i).Name + if strings.HasPrefix(progname, "Testsetup") { + progid := strings.TrimPrefix(progname, "Testsetup") + progset = append(progset, programSet{ + id: progid, + pktgen: v.FieldByName("Testpktgen" + progid).Interface().(*ebpf.Program), + setup: v.FieldByName("Testsetup" + progid).Interface().(*ebpf.Program), + check: v.FieldByName("Testcheck" + progid).Interface().(*ebpf.Program), + }) + } + } + return +} + +func consumeBpfDebugLog(t *testing.T) { + readBpfDebugLog(t) +} + +func printBpfDebugLog(t *testing.T) { + fmt.Print(readBpfDebugLog(t)) +} + +func readBpfDebugLog(t *testing.T) string { + file, err := os.Open("/sys/kernel/tracing/trace_pipe") + if err != nil { + t.Fatalf("Failed to open trace_pipe: %v", err) + } + defer file.Close() + + buffer := make([]byte, 1024*64) + n, err := file.Read(buffer) + if err != nil { + t.Fatalf("Failed to read from trace_pipe: %v", err) + } + + return string(buffer[:n]) +} + +func Test(t *testing.T) { + progsets, err := collectPrograms(t) + if err != nil { + t.Fatalf("error while collecting programs: %s", err) + } + + for _, progset := range progsets { + t.Logf("Running test: %s\n", progset.id) + // create ctx with the max allowed size(4k - head room - tailroom) + data := make([]byte, 4096-256-320) + + // sizeof(struct __sk_buff) < 256, let's make it 256 + ctx := make([]byte, 256) + + statusCode, data, ctx, err := runBpfProgram(progset.pktgen, data, ctx) + if err != nil { + t.Fatalf("error while running pktgen prog: %s", err) + } + if statusCode != 0 { + printBpfDebugLog(t) + t.Fatalf("error while running pktgen program: unexpected status code: %d", statusCode) + } + + statusCode, data, ctx, err = runBpfProgram(progset.setup, data, ctx) + if err != nil { + printBpfDebugLog(t) + t.Fatalf("error while running setup prog: %s", err) + } + + status := make([]byte, 4) + nl.NativeEndian().PutUint32(status, statusCode) + data = append(status, data...) + + statusCode, data, ctx, err = runBpfProgram(progset.check, data, ctx) + if err != nil { + t.Fatalf("error while running check program: %+v", err) + } + if statusCode != 0 { + printBpfDebugLog(t) + t.Fatalf("error while running check program: unexpected status code: %d", statusCode) + } + + consumeBpfDebugLog(t) + } +} diff --git a/control/kern/tests/bpf_test.h b/control/kern/tests/bpf_test.h new file mode 100644 index 0000000..00b7c75 --- /dev/null +++ b/control/kern/tests/bpf_test.h @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Copyright (c) 2022-2024, daeuniverse Organization + +//go:build exclude + +#define IP4_HLEN sizeof(struct iphdr) +#define IP6_HLEN sizeof(struct ipv6hdr) +#define TCP_HLEN sizeof(struct tcphdr) + +#define OUTBOUND_USER_DEFINED_MIN 2 + +#define IPV4(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +static const __u32 two_key = 2; +static const __u32 three_key = 3; +static const __u32 four_key = 4; + +static __always_inline int +set_ipv4_tcp(struct __sk_buff *skb, + __u32 saddr, __u32 daddr, + __u16 sport, __u16 dport) +{ + bpf_skb_change_tail(skb, ETH_HLEN + IP4_HLEN + TCP_HLEN, 0); + + void *data = (void *)(long)skb->data; + void *data_end = (void *)(long)skb->data_end; + + struct ethhdr *eth = data; + if ((void *)(eth + 1) > data_end) { + bpf_printk("data + sizeof(*eth) > data_end\n"); + return TC_ACT_SHOT; + } + eth->h_dest[0] = 0x0; + eth->h_dest[1] = 0x1; + eth->h_dest[2] = 0x2; + eth->h_dest[3] = 0x3; + eth->h_dest[4] = 0x4; + eth->h_dest[5] = 0x5; + eth->h_source[0] = 0x6; + eth->h_source[1] = 0x7; + eth->h_source[2] = 0x8; + eth->h_source[3] = 0x9; + eth->h_source[4] = 0xa; + eth->h_source[5] = 0xb; + eth->h_proto = bpf_htons(ETH_P_IP); + + struct iphdr *ip = data + ETH_HLEN; + if ((void *)(ip + 1) > data_end) { + bpf_printk("data + sizeof(*ip) > data_end\n"); + return TC_ACT_SHOT; + } + ip->ihl = 5; + ip->version = 4; + ip->protocol = IPPROTO_TCP; + ip->saddr = bpf_htonl(saddr); + ip->daddr = bpf_htonl(daddr); + ip->tos = 4 << 2; + + struct tcphdr *tcp = data + ETH_HLEN + IP4_HLEN; + if ((void *)(tcp + 1) > data_end) { + bpf_printk("data + sizeof(*tcp) > data_end\n"); + return TC_ACT_SHOT; + } + tcp->source = bpf_htons(sport); + tcp->dest = bpf_htons(dport); + tcp->syn = 1; + + return TC_ACT_OK; +} + +static __always_inline int +check_routing_ipv4_tcp(struct __sk_buff *skb, + __u32 expected_status_code, + __u32 saddr, __u32 daddr, + __u16 sport, __u16 dport) +{ + __u32 *status_code; + + void *data = (void *)(long)skb->data; + void *data_end = (void *)(long)skb->data_end; + + if (data + sizeof(*status_code) > data_end) { + bpf_printk("data + sizeof(*status_code) > data_end\n"); + return TC_ACT_SHOT; + } + + status_code = data; + if (*status_code != expected_status_code) { + bpf_printk("status_code(%d) != %d\n", *status_code, expected_status_code); + return TC_ACT_SHOT; + } + + if (expected_status_code == TC_ACT_REDIRECT) { + if (skb->cb[0] != TPROXY_MARK) { + bpf_printk("skb->cb[0] != TPROXY_MARK\n"); + return TC_ACT_SHOT; + } + + if (skb->cb[1] != IPPROTO_TCP) { + bpf_printk("skb->cb[1] != IPPROTO_TCP\n"); + return TC_ACT_SHOT; + } + + } + + struct ethhdr *eth = data + sizeof(*status_code); + if ((void *)(eth + 1) > data_end) { + bpf_printk("data + sizeof(*eth) > data_end\n"); + return TC_ACT_SHOT; + } + if (eth->h_proto != bpf_htons(ETH_P_IP)) { + bpf_printk("eth->h_proto != ETH_P_IP\n"); + return TC_ACT_SHOT; + } + + struct iphdr *ip = (void *)eth + ETH_HLEN; + if ((void *)(ip + 1) > data_end) { + bpf_printk("data + sizeof(*ip) > data_end\n"); + return TC_ACT_SHOT; + } + if (ip->protocol != IPPROTO_TCP) { + bpf_printk("ip->protocol != IPPROTO_TCP\n"); + return TC_ACT_SHOT; + } + if (ip->saddr != bpf_htonl(saddr)) { + bpf_printk("ip->saddr != %pI4\n", &saddr); + return TC_ACT_SHOT; + } + if (ip->daddr != bpf_htonl(daddr)) { + bpf_printk("ip->daddr != %pI4\n", &daddr); + return TC_ACT_SHOT; + } + + struct tcphdr *tcp = (void *)ip + IP4_HLEN; + if ((void *)(tcp + 1) > data_end) { + bpf_printk("data + sizeof(*tcp) > data_end\n"); + return TC_ACT_SHOT; + } + if (tcp->source != bpf_htons(sport)) { + bpf_printk("tcp->source != %d\n", sport); + return TC_ACT_SHOT; + } + if (tcp->dest != bpf_htons(dport)) { + bpf_printk("tcp->dest != %d\n", dport); + return TC_ACT_SHOT; + } + + if (expected_status_code == TC_ACT_REDIRECT) { + struct tuples tuples = {}; + tuples.five.sip.u6_addr32[2] = bpf_htonl(0xffff); + tuples.five.sip.u6_addr32[3] = ip->saddr; + tuples.five.dip.u6_addr32[2] = bpf_htonl(0xffff); + tuples.five.dip.u6_addr32[3] = ip->daddr; + tuples.five.sport = tcp->source; + tuples.five.dport = tcp->dest; + tuples.five.l4proto = ip->protocol; + + struct routing_result *routing_result; + routing_result = bpf_map_lookup_elem(&routing_tuples_map, &tuples.five); + if (!routing_result) { + bpf_printk("routing_result == NULL\n"); + return TC_ACT_SHOT; + } + + if (routing_result->outbound != OUTBOUND_USER_DEFINED_MIN) { + bpf_printk("routing_result->outbound != OUTBOUND_USER_DEFINED_MIN\n"); + return TC_ACT_SHOT; + } + } + + return TC_ACT_OK; +} + +static __always_inline void +set_routing_fallback(__u8 outbound, bool must) +{ + struct match_set ms = {}; + ms.not = false; + ms.type = MatchType_Fallback; + ms.outbound = outbound; + ms.must = must; + ms.mark = 0; + bpf_map_update_elem(&routing_map, &one_key, &ms, BPF_ANY); +}