1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2020 Jesper Dangaard Brouer */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include <linux/if_ether.h> 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 char _license[] SEC("license") = "GPL"; 12 13 /* Userspace will update with MTU it can see on device */ 14 static volatile const int GLOBAL_USER_MTU; 15 static volatile const __u32 GLOBAL_USER_IFINDEX; 16 17 /* BPF-prog will update these with MTU values it can see */ 18 __u32 global_bpf_mtu_xdp = 0; 19 __u32 global_bpf_mtu_tc = 0; 20 21 SEC("xdp") 22 int xdp_use_helper_basic(struct xdp_md *ctx) 23 { 24 __u32 mtu_len = 0; 25 26 if (bpf_check_mtu(ctx, 0, &mtu_len, 0, 0)) 27 return XDP_ABORTED; 28 29 return XDP_PASS; 30 } 31 32 SEC("xdp") 33 int xdp_use_helper(struct xdp_md *ctx) 34 { 35 int retval = XDP_PASS; /* Expected retval on successful test */ 36 __u32 mtu_len = 0; 37 __u32 ifindex = 0; 38 int delta = 0; 39 40 /* When ifindex is zero, save net_device lookup and use ctx netdev */ 41 if (GLOBAL_USER_IFINDEX > 0) 42 ifindex = GLOBAL_USER_IFINDEX; 43 44 if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) { 45 /* mtu_len is also valid when check fail */ 46 retval = XDP_ABORTED; 47 goto out; 48 } 49 50 if (mtu_len != GLOBAL_USER_MTU) 51 retval = XDP_DROP; 52 53 out: 54 global_bpf_mtu_xdp = mtu_len; 55 return retval; 56 } 57 58 SEC("xdp") 59 int xdp_exceed_mtu(struct xdp_md *ctx) 60 { 61 void *data_end = (void *)(long)ctx->data_end; 62 void *data = (void *)(long)ctx->data; 63 __u32 ifindex = GLOBAL_USER_IFINDEX; 64 __u32 data_len = data_end - data; 65 int retval = XDP_ABORTED; /* Fail */ 66 __u32 mtu_len = 0; 67 int delta; 68 int err; 69 70 /* Exceed MTU with 1 via delta adjust */ 71 delta = GLOBAL_USER_MTU - (data_len - ETH_HLEN) + 1; 72 73 err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); 74 if (err) { 75 retval = XDP_PASS; /* Success in exceeding MTU check */ 76 if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) 77 retval = XDP_DROP; 78 } 79 80 global_bpf_mtu_xdp = mtu_len; 81 return retval; 82 } 83 84 SEC("xdp") 85 int xdp_minus_delta(struct xdp_md *ctx) 86 { 87 int retval = XDP_PASS; /* Expected retval on successful test */ 88 void *data_end = (void *)(long)ctx->data_end; 89 void *data = (void *)(long)ctx->data; 90 __u32 ifindex = GLOBAL_USER_IFINDEX; 91 __u32 data_len = data_end - data; 92 __u32 mtu_len = 0; 93 int delta; 94 95 /* Borderline test case: Minus delta exceeding packet length allowed */ 96 delta = -((data_len - ETH_HLEN) + 1); 97 98 /* Minus length (adjusted via delta) still pass MTU check, other helpers 99 * are responsible for catching this, when doing actual size adjust 100 */ 101 if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) 102 retval = XDP_ABORTED; 103 104 global_bpf_mtu_xdp = mtu_len; 105 return retval; 106 } 107 108 SEC("classifier") 109 int tc_use_helper(struct __sk_buff *ctx) 110 { 111 int retval = BPF_OK; /* Expected retval on successful test */ 112 __u32 mtu_len = 0; 113 int delta = 0; 114 115 if (bpf_check_mtu(ctx, 0, &mtu_len, delta, 0)) { 116 retval = BPF_DROP; 117 goto out; 118 } 119 120 if (mtu_len != GLOBAL_USER_MTU) 121 retval = BPF_REDIRECT; 122 out: 123 global_bpf_mtu_tc = mtu_len; 124 return retval; 125 } 126 127 SEC("classifier") 128 int tc_exceed_mtu(struct __sk_buff *ctx) 129 { 130 __u32 ifindex = GLOBAL_USER_IFINDEX; 131 int retval = BPF_DROP; /* Fail */ 132 __u32 skb_len = ctx->len; 133 __u32 mtu_len = 0; 134 int delta; 135 int err; 136 137 /* Exceed MTU with 1 via delta adjust */ 138 delta = GLOBAL_USER_MTU - (skb_len - ETH_HLEN) + 1; 139 140 err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); 141 if (err) { 142 retval = BPF_OK; /* Success in exceeding MTU check */ 143 if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) 144 retval = BPF_DROP; 145 } 146 147 global_bpf_mtu_tc = mtu_len; 148 return retval; 149 } 150 151 SEC("classifier") 152 int tc_exceed_mtu_da(struct __sk_buff *ctx) 153 { 154 /* SKB Direct-Access variant */ 155 void *data_end = (void *)(long)ctx->data_end; 156 void *data = (void *)(long)ctx->data; 157 __u32 ifindex = GLOBAL_USER_IFINDEX; 158 __u32 data_len = data_end - data; 159 int retval = BPF_DROP; /* Fail */ 160 __u32 mtu_len = 0; 161 int delta; 162 int err; 163 164 /* Exceed MTU with 1 via delta adjust */ 165 delta = GLOBAL_USER_MTU - (data_len - ETH_HLEN) + 1; 166 167 err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); 168 if (err) { 169 retval = BPF_OK; /* Success in exceeding MTU check */ 170 if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) 171 retval = BPF_DROP; 172 } 173 174 global_bpf_mtu_tc = mtu_len; 175 return retval; 176 } 177 178 SEC("classifier") 179 int tc_minus_delta(struct __sk_buff *ctx) 180 { 181 int retval = BPF_OK; /* Expected retval on successful test */ 182 __u32 ifindex = GLOBAL_USER_IFINDEX; 183 __u32 skb_len = ctx->len; 184 __u32 mtu_len = 0; 185 int delta; 186 187 /* Borderline test case: Minus delta exceeding packet length allowed */ 188 delta = -((skb_len - ETH_HLEN) + 1); 189 190 /* Minus length (adjusted via delta) still pass MTU check, other helpers 191 * are responsible for catching this, when doing actual size adjust 192 */ 193 if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) 194 retval = BPF_DROP; 195 196 global_bpf_mtu_xdp = mtu_len; 197 return retval; 198 } 199