|
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | +/* |
| 3 | + * This file contains functions that are used in the packetXX XDP programs to |
| 4 | + * manipulate on packets data. The functions are marked as __always_inline, and |
| 5 | + * fully defined in this header file to be included in the BPF program. |
| 6 | + */ |
| 7 | + |
| 8 | +#ifndef __REWRITE_HELPERS_H |
| 9 | +#define __REWRITE_HELPERS_H |
| 10 | + |
| 11 | +#include <linux/bpf.h> |
| 12 | +#include <linux/ip.h> |
| 13 | +#include <linux/ipv6.h> |
| 14 | +#include <linux/if_ether.h> |
| 15 | + |
| 16 | +#include "bpf_helpers.h" |
| 17 | +#include "bpf_endian.h" |
| 18 | + |
| 19 | +/* Pops the outermost VLAN tag off the packet. Returns the popped VLAN ID on |
| 20 | + * success or negative errno on failure. |
| 21 | + */ |
| 22 | +static __always_inline int vlan_tag_pop(struct xdp_md *ctx, struct ethhdr *eth) |
| 23 | +{ |
| 24 | + void *data_end = (void *)(long)ctx->data_end; |
| 25 | + struct ethhdr eth_cpy; |
| 26 | + struct vlan_hdr *vlh; |
| 27 | + __be16 h_proto; |
| 28 | + int vlid; |
| 29 | + |
| 30 | + if (!proto_is_vlan(eth->h_proto)) |
| 31 | + return -1; |
| 32 | + |
| 33 | + /* Careful with the parenthesis here */ |
| 34 | + vlh = (void *)(eth + 1); |
| 35 | + |
| 36 | + /* Still need to do bounds checking */ |
| 37 | + if (vlh + 1 > data_end) |
| 38 | + return -1; |
| 39 | + |
| 40 | + /* Save vlan ID for returning, h_proto for updating Ethernet header */ |
| 41 | + vlid = bpf_ntohs(vlh->h_vlan_TCI); |
| 42 | + h_proto = vlh->h_vlan_encapsulated_proto; |
| 43 | + |
| 44 | + /* Make a copy of the outer Ethernet header before we cut it off */ |
| 45 | + __builtin_memcpy(ð_cpy, eth, sizeof(eth_cpy)); |
| 46 | + |
| 47 | + /* Actually adjust the head pointer */ |
| 48 | + if (bpf_xdp_adjust_head(ctx, (int)sizeof(*vlh))) |
| 49 | + return -1; |
| 50 | + |
| 51 | + /* Need to re-evaluate data *and* data_end and do new bounds checking |
| 52 | + * after adjusting head |
| 53 | + */ |
| 54 | + eth = (void *)(long)ctx->data; |
| 55 | + data_end = (void *)(long)ctx->data_end; |
| 56 | + if (eth + 1 > data_end) |
| 57 | + return -1; |
| 58 | + |
| 59 | + /* Copy back the old Ethernet header and update the proto type */ |
| 60 | + __builtin_memcpy(eth, ð_cpy, sizeof(*eth)); |
| 61 | + eth->h_proto = h_proto; |
| 62 | + |
| 63 | + return vlid; |
| 64 | +} |
| 65 | + |
| 66 | +/* Pushes a new VLAN tag after the Ethernet header. Returns 0 on success, |
| 67 | + * -1 on failure. |
| 68 | + */ |
| 69 | +static __always_inline int vlan_tag_push(struct xdp_md *ctx, |
| 70 | + struct ethhdr *eth, int vlid) |
| 71 | +{ |
| 72 | + void *data_end = (void *)(long)ctx->data_end; |
| 73 | + struct ethhdr eth_cpy; |
| 74 | + struct vlan_hdr *vlh; |
| 75 | + |
| 76 | + /* First copy the original Ethernet header */ |
| 77 | + __builtin_memcpy(ð_cpy, eth, sizeof(eth_cpy)); |
| 78 | + |
| 79 | + /* Then add space in front of the packet */ |
| 80 | + if (bpf_xdp_adjust_head(ctx, 0 - (int)sizeof(*vlh))) |
| 81 | + return -1; |
| 82 | + |
| 83 | + /* Need to re-evaluate data_end and data after head adjustment, and |
| 84 | + * bounds check, even though we know there is enough space (as we |
| 85 | + * increased it). |
| 86 | + */ |
| 87 | + data_end = (void *)(long)ctx->data_end; |
| 88 | + eth = (void *)(long)ctx->data; |
| 89 | + |
| 90 | + if (eth + 1 > data_end) |
| 91 | + return -1; |
| 92 | + |
| 93 | + /* Copy back the Ethernet header in the right place, populate the VLAN |
| 94 | + * tag with the ID and proto, and set the outer Ethernet header to VLAN |
| 95 | + * type. */ |
| 96 | + __builtin_memcpy(eth, ð_cpy, sizeof(*eth)); |
| 97 | + |
| 98 | + vlh = (void *)(eth +1); |
| 99 | + |
| 100 | + if (vlh + 1 > data_end) |
| 101 | + return -1; |
| 102 | + |
| 103 | + vlh->h_vlan_TCI = bpf_htons(vlid); |
| 104 | + vlh->h_vlan_encapsulated_proto = eth->h_proto; |
| 105 | + |
| 106 | + eth->h_proto = bpf_htons(ETH_P_8021Q); |
| 107 | + return 0; |
| 108 | +} |
| 109 | + |
| 110 | +/* |
| 111 | + * Swaps destination and source MAC addresses inside an Ethernet header |
| 112 | + */ |
| 113 | +static __always_inline void swap_src_dst_mac(struct ethhdr *eth) |
| 114 | +{ |
| 115 | + __u8 h_tmp[ETH_ALEN]; |
| 116 | + __builtin_memcpy(h_tmp, eth->h_source, ETH_ALEN); |
| 117 | + __builtin_memcpy(eth->h_source, eth->h_dest, ETH_ALEN); |
| 118 | + __builtin_memcpy(eth->h_dest, h_tmp, ETH_ALEN); |
| 119 | +} |
| 120 | + |
| 121 | +/* |
| 122 | + * Swaps destination and source IPv6 addresses inside an IPv6 header |
| 123 | + */ |
| 124 | +static __always_inline void swap_src_dst_ipv6(struct ipv6hdr *ipv6) |
| 125 | +{ |
| 126 | + struct in6_addr tmp = ipv6->saddr; |
| 127 | + ipv6->saddr = ipv6->daddr; |
| 128 | + ipv6->daddr = tmp; |
| 129 | +} |
| 130 | + |
| 131 | +/* |
| 132 | + * Swaps destination and source IPv4 addresses inside an IPv4 header |
| 133 | + */ |
| 134 | +static __always_inline void swap_src_dst_ipv4(struct iphdr *iphdr) |
| 135 | +{ |
| 136 | + __be32 tmp = iphdr->saddr; |
| 137 | + iphdr->saddr = iphdr->daddr; |
| 138 | + iphdr->daddr = tmp; |
| 139 | +} |
| 140 | + |
| 141 | +#endif /* __REWRITE_HELPERS_H */ |
0 commit comments