summaryrefslogtreecommitdiff
path: root/src/xdp/common/rewrite_helpers.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/xdp/common/rewrite_helpers.h')
-rw-r--r--src/xdp/common/rewrite_helpers.h144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/xdp/common/rewrite_helpers.h b/src/xdp/common/rewrite_helpers.h
new file mode 100644
index 000000000..a5d3e671d
--- /dev/null
+++ b/src/xdp/common/rewrite_helpers.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-clause) */
+/*
+ * This file contains functions that are used in the packetXX XDP programs to
+ * manipulate on packets data. The functions are marked as __always_inline, and
+ * fully defined in this header file to be included in the BPF program.
+ */
+
+#ifndef __REWRITE_HELPERS_H
+#define __REWRITE_HELPERS_H
+
+#include <linux/bpf.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/if_ether.h>
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+/* Pops the outermost VLAN tag off the packet. Returns the popped VLAN ID on
+ * success or negative errno on failure.
+ */
+static __always_inline int vlan_tag_pop(struct xdp_md *ctx, struct ethhdr *eth)
+{
+ void *data_end = (void *)(long)ctx->data_end;
+ struct ethhdr eth_cpy;
+ struct vlan_hdr *vlh;
+ __be16 h_proto;
+ int vlid;
+
+ if (!proto_is_vlan(eth->h_proto))
+ return -1;
+
+ /* Careful with the parenthesis here */
+ vlh = (void *)(eth + 1);
+
+ /* Still need to do bounds checking */
+ if (vlh + 1 > data_end)
+ return -1;
+
+ /* Save vlan ID for returning, h_proto for updating Ethernet header */
+ vlid = bpf_ntohs(vlh->h_vlan_TCI);
+ h_proto = vlh->h_vlan_encapsulated_proto;
+
+ /* Make a copy of the outer Ethernet header before we cut it off */
+ __builtin_memcpy(&eth_cpy, eth, sizeof(eth_cpy));
+
+ /* Actually adjust the head pointer */
+ if (bpf_xdp_adjust_head(ctx, (int)sizeof(*vlh)))
+ return -1;
+
+ /* Need to re-evaluate data *and* data_end and do new bounds checking
+ * after adjusting head
+ */
+ eth = (void *)(long)ctx->data;
+ data_end = (void *)(long)ctx->data_end;
+ if (eth + 1 > data_end)
+ return -1;
+
+ /* Copy back the old Ethernet header and update the proto type */
+ __builtin_memcpy(eth, &eth_cpy, sizeof(*eth));
+ eth->h_proto = h_proto;
+
+ return vlid;
+}
+
+/* Pushes a new VLAN tag after the Ethernet header. Returns 0 on success,
+ * -1 on failure.
+ */
+static __always_inline int vlan_tag_push(struct xdp_md *ctx,
+ struct ethhdr *eth, int vlid)
+{
+ void *data_end = (void *)(long)ctx->data_end;
+ struct ethhdr eth_cpy;
+ struct vlan_hdr *vlh;
+
+ /* First copy the original Ethernet header */
+ __builtin_memcpy(&eth_cpy, eth, sizeof(eth_cpy));
+
+ /* Then add space in front of the packet */
+ if (bpf_xdp_adjust_head(ctx, 0 - (int)sizeof(*vlh)))
+ return -1;
+
+ /* Need to re-evaluate data_end and data after head adjustment, and
+ * bounds check, even though we know there is enough space (as we
+ * increased it).
+ */
+ data_end = (void *)(long)ctx->data_end;
+ eth = (void *)(long)ctx->data;
+
+ if (eth + 1 > data_end)
+ return -1;
+
+ /* Copy back Ethernet header in the right place, populate VLAN tag with
+ * ID and proto, and set outer Ethernet header to VLAN type.
+ */
+ __builtin_memcpy(eth, &eth_cpy, sizeof(*eth));
+
+ vlh = (void *)(eth + 1);
+
+ if (vlh + 1 > data_end)
+ return -1;
+
+ vlh->h_vlan_TCI = bpf_htons(vlid);
+ vlh->h_vlan_encapsulated_proto = eth->h_proto;
+
+ eth->h_proto = bpf_htons(ETH_P_8021Q);
+ return 0;
+}
+
+/*
+ * Swaps destination and source MAC addresses inside an Ethernet header
+ */
+static __always_inline void swap_src_dst_mac(struct ethhdr *eth)
+{
+ __u8 h_tmp[ETH_ALEN];
+
+ __builtin_memcpy(h_tmp, eth->h_source, ETH_ALEN);
+ __builtin_memcpy(eth->h_source, eth->h_dest, ETH_ALEN);
+ __builtin_memcpy(eth->h_dest, h_tmp, ETH_ALEN);
+}
+
+/*
+ * Swaps destination and source IPv6 addresses inside an IPv6 header
+ */
+static __always_inline void swap_src_dst_ipv6(struct ipv6hdr *ipv6)
+{
+ struct in6_addr tmp = ipv6->saddr;
+
+ ipv6->saddr = ipv6->daddr;
+ ipv6->daddr = tmp;
+}
+
+/*
+ * Swaps destination and source IPv4 addresses inside an IPv4 header
+ */
+static __always_inline void swap_src_dst_ipv4(struct iphdr *iphdr)
+{
+ __be32 tmp = iphdr->saddr;
+
+ iphdr->saddr = iphdr->daddr;
+ iphdr->daddr = tmp;
+}
+
+#endif /* __REWRITE_HELPERS_H */