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.zaka...@gmail.com>
---
 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

Reply via email to