Foo over UDP uses UDP encapsulation to add additional entropy into the packets so that they get beter distribution across EMCP routes. Expose udp_flow_src_port as a bpf helper so that tunnel filters can benefit from the helper. Signed-off-by: Farid Zakaria <farid.m.zakaria@xxxxxxxxx> --- include/uapi/linux/bpf.h | 21 +++++++-- net/core/filter.c | 20 ++++++++ tools/include/uapi/linux/bpf.h | 21 +++++++-- tools/testing/selftests/bpf/bpf_helpers.h | 2 + .../bpf/prog_tests/udp_flow_src_port.c | 28 +++++++++++ .../bpf/progs/test_udp_flow_src_port_kern.c | 47 +++++++++++++++++++ 6 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/udp_flow_src_port.c create mode 100644 tools/testing/selftests/bpf/progs/test_udp_flow_src_port_kern.c diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 4393bd4b2419..90e814153dec 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2545,9 +2545,21 @@ union bpf_attr { * *th* points to the start of the TCP header, while *th_len* * contains **sizeof**\ (**struct tcphdr**). * - * Return - * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative - * error otherwise. + * Return + * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative + * error otherwise. + * + * int bpf_udp_flow_src_port(struct sk_buff *skb, int min, int max, int use_eth) + * Description + * It's common to implement tunnelling inside a UDP protocol to provide + * additional randomness to the packet. The destination port of the UDP + * header indicates the inner packet type whereas the source port is used + * for additional entropy. + * + * Return + * An obfuscated hash of the packet that falls within the + * min & max port range. + * If min >= max, the default port range is used * * int bpf_sysctl_get_name(struct bpf_sysctl *ctx, char *buf, size_t buf_len, u64 flags) * Description @@ -2853,7 +2865,8 @@ union bpf_attr { FN(sk_storage_get), \ FN(sk_storage_delete), \ FN(send_signal), \ - FN(tcp_gen_syncookie), + FN(tcp_gen_syncookie), \ + FN(udp_flow_src_port), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index 5a2707918629..fdf0ebb8c2c8 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2341,6 +2341,24 @@ static const struct bpf_func_proto bpf_msg_pull_data_proto = { .arg4_type = ARG_ANYTHING, }; +BPF_CALL_4(bpf_udp_flow_src_port, struct sk_buff *, skb, int, min, + int, max, int, use_eth) +{ + struct net *net = dev_net(skb->dev); + + return udp_flow_src_port(net, skb, min, max, use_eth); +} + +static const struct bpf_func_proto bpf_udp_flow_src_port_proto = { + .func = bpf_udp_flow_src_port, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_ANYTHING, +}; + BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, u32, len, u64, flags) { @@ -6186,6 +6204,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_sk_storage_get_proto; case BPF_FUNC_sk_storage_delete: return &bpf_sk_storage_delete_proto; + case BPF_FUNC_udp_flow_src_port: + return &bpf_udp_flow_src_port_proto; #ifdef CONFIG_XFRM case BPF_FUNC_skb_get_xfrm_state: return &bpf_skb_get_xfrm_state_proto; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 4393bd4b2419..90e814153dec 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -2545,9 +2545,21 @@ union bpf_attr { * *th* points to the start of the TCP header, while *th_len* * contains **sizeof**\ (**struct tcphdr**). * - * Return - * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative - * error otherwise. + * Return + * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative + * error otherwise. + * + * int bpf_udp_flow_src_port(struct sk_buff *skb, int min, int max, int use_eth) + * Description + * It's common to implement tunnelling inside a UDP protocol to provide + * additional randomness to the packet. The destination port of the UDP + * header indicates the inner packet type whereas the source port is used + * for additional entropy. + * + * Return + * An obfuscated hash of the packet that falls within the + * min & max port range. + * If min >= max, the default port range is used * * int bpf_sysctl_get_name(struct bpf_sysctl *ctx, char *buf, size_t buf_len, u64 flags) * Description @@ -2853,7 +2865,8 @@ union bpf_attr { FN(sk_storage_get), \ FN(sk_storage_delete), \ FN(send_signal), \ - FN(tcp_gen_syncookie), + FN(tcp_gen_syncookie), \ + FN(udp_flow_src_port), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 120aa86c58d3..385bfd8b7436 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -313,6 +313,8 @@ static unsigned int (*bpf_set_hash)(void *ctx, __u32 hash) = static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode, unsigned long long flags) = (void *) BPF_FUNC_skb_adjust_room; +static int (*bpf_udp_flow_src_port)(void *ctx, int min, int max, int use_eth) = + (void *) BPF_FUNC_udp_flow_src_port; /* Scan the ARCH passed in from ARCH env variable (see Makefile) */ #if defined(__TARGET_ARCH_x86) diff --git a/tools/testing/selftests/bpf/prog_tests/udp_flow_src_port.c b/tools/testing/selftests/bpf/prog_tests/udp_flow_src_port.c new file mode 100644 index 000000000000..0f7303b51d1d --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/udp_flow_src_port.c @@ -0,0 +1,28 @@ +#include <test_progs.h> +#include <linux/pkt_cls.h> + +void test_udp_flow_src_port(void) +{ + const char *file = "./test_udp_flow_src_port_kern.o"; + struct bpf_object *obj; + __u32 duration, retval, size; + int err, prog_fd; + char buf[128]; + struct tcphdr *tcph = (void *)buf + sizeof(struct ethhdr) + sizeof(struct iphdr); + + err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); + if (err) { + error_cnt++; + return; + } + + short original = tcph->source; + + err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), + buf, &size, &retval, &duration); + CHECK(err || retval != TC_ACT_OK || tcph->source == original, "ipv4", + "err %d errno %d retval %d sport %d duration %d\n", + err, errno, retval, tcph->source, duration); + + bpf_object__close(obj); +} diff --git a/tools/testing/selftests/bpf/progs/test_udp_flow_src_port_kern.c b/tools/testing/selftests/bpf/progs/test_udp_flow_src_port_kern.c new file mode 100644 index 000000000000..6238bd5fa856 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_udp_flow_src_port_kern.c @@ -0,0 +1,47 @@ +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#include <linux/bpf.h> +#include <linux/if_ether.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/pkt_cls.h> +#include <linux/tcp.h> +#include "bpf_helpers.h" +#include "bpf_endian.h" + +int _version SEC("version") = 1; +char _license[] SEC("license") = "GPL"; + +SEC("skb_udp_flow") +int process(struct __sk_buff *skb) +{ + void *data = (void *)(size_t)skb->data; + void *data_end = (void *)(size_t)skb->data_end; + /* Is it an Ethernet frame? */ + struct ethhdr *ethernet_header = (struct ethhdr *)data; + data += sizeof(*ethernet_header); + if (data > data_end) { + return TC_ACT_SHOT; + } + + struct iphdr *ip_header = (struct iphdr *)data; + data += sizeof(*ip_header); + if (data > data_end) { + return TC_ACT_SHOT; + } + + struct tcphdr *tcp_header = (struct tcphdr*)data; + data += sizeof(*tcp_header); + if (data > data_end) { + return TC_ACT_SHOT; + } + + //lets assign the calculated source port in the + // tcp packet and verify it in the test + int sport = bpf_udp_flow_src_port(skb, 0, 0, 0); + tcp_header->source = sport; + + return TC_ACT_OK; +} -- 2.21.0