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 #include <errno.h> 11 12 char _license[] SEC("license") = "GPL"; 13 14 /* Userspace will update with MTU it can see on device */ 15 volatile const int GLOBAL_USER_MTU; 16 volatile const __u32 GLOBAL_USER_IFINDEX; 17 18 /* BPF-prog will update these with MTU values it can see */ 19 __u32 global_bpf_mtu_xdp = 0; 20 __u32 global_bpf_mtu_tc = 0; 21 22 SEC("xdp") 23 int xdp_use_helper_basic(struct xdp_md *ctx) 24 { 25 __u32 mtu_len = 0; 26 27 if (bpf_check_mtu(ctx, 0, &mtu_len, 0, 0)) 28 return XDP_ABORTED; 29 30 return XDP_PASS; 31 } 32 33 SEC("xdp") 34 int xdp_use_helper(struct xdp_md *ctx) 35 { 36 int retval = XDP_PASS; /* Expected retval on successful test */ 37 __u32 mtu_len = 0; 38 __u32 ifindex = 0; 39 int delta = 0; 40 41 /* When ifindex is zero, save net_device lookup and use ctx netdev */ 42 if (GLOBAL_USER_IFINDEX > 0) 43 ifindex = GLOBAL_USER_IFINDEX; 44 45 if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) { 46 /* mtu_len is also valid when check fail */ 47 retval = XDP_ABORTED; 48 goto out; 49 } 50 51 if (mtu_len != GLOBAL_USER_MTU) 52 retval = XDP_DROP; 53 54 out: 55 global_bpf_mtu_xdp = mtu_len; 56 return retval; 57 } 58 59 SEC("xdp") 60 int xdp_exceed_mtu(struct xdp_md *ctx) 61 { 62 void *data_end = (void *)(long)ctx->data_end; 63 void *data = (void *)(long)ctx->data; 64 __u32 ifindex = GLOBAL_USER_IFINDEX; 65 __u32 data_len = data_end - data; 66 int retval = XDP_ABORTED; /* Fail */ 67 __u32 mtu_len = 0; 68 int delta; 69 int err; 70 71 /* Exceed MTU with 1 via delta adjust */ 72 delta = GLOBAL_USER_MTU - (data_len - ETH_HLEN) + 1; 73 74 err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); 75 if (err) { 76 retval = XDP_PASS; /* Success in exceeding MTU check */ 77 if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) 78 retval = XDP_DROP; 79 } 80 81 global_bpf_mtu_xdp = mtu_len; 82 return retval; 83 } 84 85 SEC("xdp") 86 int xdp_minus_delta(struct xdp_md *ctx) 87 { 88 int retval = XDP_PASS; /* Expected retval on successful test */ 89 void *data_end = (void *)(long)ctx->data_end; 90 void *data = (void *)(long)ctx->data; 91 __u32 ifindex = GLOBAL_USER_IFINDEX; 92 __u32 data_len = data_end - data; 93 __u32 mtu_len = 0; 94 int delta; 95 96 /* Borderline test case: Minus delta exceeding packet length allowed */ 97 delta = -((data_len - ETH_HLEN) + 1); 98 99 /* Minus length (adjusted via delta) still pass MTU check, other helpers 100 * are responsible for catching this, when doing actual size adjust 101 */ 102 if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) 103 retval = XDP_ABORTED; 104 105 global_bpf_mtu_xdp = mtu_len; 106 return retval; 107 } 108 109 SEC("xdp") 110 int xdp_input_len(struct xdp_md *ctx) 111 { 112 int retval = XDP_PASS; /* Expected retval on successful test */ 113 void *data_end = (void *)(long)ctx->data_end; 114 void *data = (void *)(long)ctx->data; 115 __u32 ifindex = GLOBAL_USER_IFINDEX; 116 __u32 data_len = data_end - data; 117 118 /* API allow user give length to check as input via mtu_len param, 119 * resulting MTU value is still output in mtu_len param after call. 120 * 121 * Input len is L3, like MTU and iph->tot_len. 122 * Remember XDP data_len is L2. 123 */ 124 __u32 mtu_len = data_len - ETH_HLEN; 125 126 if (bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0)) 127 retval = XDP_ABORTED; 128 129 global_bpf_mtu_xdp = mtu_len; 130 return retval; 131 } 132 133 SEC("xdp") 134 int xdp_input_len_exceed(struct xdp_md *ctx) 135 { 136 int retval = XDP_ABORTED; /* Fail */ 137 __u32 ifindex = GLOBAL_USER_IFINDEX; 138 int err; 139 140 /* API allow user give length to check as input via mtu_len param, 141 * resulting MTU value is still output in mtu_len param after call. 142 * 143 * Input length value is L3 size like MTU. 144 */ 145 __u32 mtu_len = GLOBAL_USER_MTU; 146 147 mtu_len += 1; /* Exceed with 1 */ 148 149 err = bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0); 150 if (err == BPF_MTU_CHK_RET_FRAG_NEEDED) 151 retval = XDP_PASS ; /* Success in exceeding MTU check */ 152 153 global_bpf_mtu_xdp = mtu_len; 154 return retval; 155 } 156 157 SEC("tc") 158 int tc_use_helper(struct __sk_buff *ctx) 159 { 160 int retval = BPF_OK; /* Expected retval on successful test */ 161 __u32 mtu_len = 0; 162 int delta = 0; 163 164 if (bpf_check_mtu(ctx, 0, &mtu_len, delta, 0)) { 165 retval = BPF_DROP; 166 goto out; 167 } 168 169 if (mtu_len != GLOBAL_USER_MTU) 170 retval = BPF_REDIRECT; 171 out: 172 global_bpf_mtu_tc = mtu_len; 173 return retval; 174 } 175 176 SEC("tc") 177 int tc_exceed_mtu(struct __sk_buff *ctx) 178 { 179 __u32 ifindex = GLOBAL_USER_IFINDEX; 180 int retval = BPF_DROP; /* Fail */ 181 __u32 skb_len = ctx->len; 182 __u32 mtu_len = 0; 183 int delta; 184 int err; 185 186 /* Exceed MTU with 1 via delta adjust */ 187 delta = GLOBAL_USER_MTU - (skb_len - ETH_HLEN) + 1; 188 189 err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); 190 if (err) { 191 retval = BPF_OK; /* Success in exceeding MTU check */ 192 if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) 193 retval = BPF_DROP; 194 } 195 196 global_bpf_mtu_tc = mtu_len; 197 return retval; 198 } 199 200 SEC("tc") 201 int tc_exceed_mtu_da(struct __sk_buff *ctx) 202 { 203 /* SKB Direct-Access variant */ 204 void *data_end = (void *)(long)ctx->data_end; 205 void *data = (void *)(long)ctx->data; 206 __u32 ifindex = GLOBAL_USER_IFINDEX; 207 __u32 data_len = data_end - data; 208 int retval = BPF_DROP; /* Fail */ 209 __u32 mtu_len = 0; 210 int delta; 211 int err; 212 213 /* Exceed MTU with 1 via delta adjust */ 214 delta = GLOBAL_USER_MTU - (data_len - ETH_HLEN) + 1; 215 216 err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); 217 if (err) { 218 retval = BPF_OK; /* Success in exceeding MTU check */ 219 if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) 220 retval = BPF_DROP; 221 } 222 223 global_bpf_mtu_tc = mtu_len; 224 return retval; 225 } 226 227 SEC("tc") 228 int tc_minus_delta(struct __sk_buff *ctx) 229 { 230 int retval = BPF_OK; /* Expected retval on successful test */ 231 __u32 ifindex = GLOBAL_USER_IFINDEX; 232 __u32 skb_len = ctx->len; 233 __u32 mtu_len = 0; 234 int delta; 235 236 /* Borderline test case: Minus delta exceeding packet length allowed */ 237 delta = -((skb_len - ETH_HLEN) + 1); 238 239 /* Minus length (adjusted via delta) still pass MTU check, other helpers 240 * are responsible for catching this, when doing actual size adjust 241 */ 242 if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) 243 retval = BPF_DROP; 244 245 global_bpf_mtu_xdp = mtu_len; 246 return retval; 247 } 248 249 SEC("tc") 250 int tc_input_len(struct __sk_buff *ctx) 251 { 252 int retval = BPF_OK; /* Expected retval on successful test */ 253 __u32 ifindex = GLOBAL_USER_IFINDEX; 254 255 /* API allow user give length to check as input via mtu_len param, 256 * resulting MTU value is still output in mtu_len param after call. 257 * 258 * Input length value is L3 size. 259 */ 260 __u32 mtu_len = GLOBAL_USER_MTU; 261 262 if (bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0)) 263 retval = BPF_DROP; 264 265 global_bpf_mtu_xdp = mtu_len; 266 return retval; 267 } 268 269 SEC("tc") 270 int tc_input_len_exceed(struct __sk_buff *ctx) 271 { 272 int retval = BPF_DROP; /* Fail */ 273 __u32 ifindex = GLOBAL_USER_IFINDEX; 274 int err; 275 276 /* API allow user give length to check as input via mtu_len param, 277 * resulting MTU value is still output in mtu_len param after call. 278 * 279 * Input length value is L3 size like MTU. 280 */ 281 __u32 mtu_len = GLOBAL_USER_MTU; 282 283 mtu_len += 1; /* Exceed with 1 */ 284 285 err = bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0); 286 if (err == BPF_MTU_CHK_RET_FRAG_NEEDED) 287 retval = BPF_OK; /* Success in exceeding MTU check */ 288 289 global_bpf_mtu_xdp = mtu_len; 290 return retval; 291 } 292 293 SEC("tc") 294 int tc_chk_segs_flag(struct __sk_buff *ctx) 295 { 296 __u32 mtu_len = 0; 297 int err; 298 299 err = bpf_check_mtu(ctx, GLOBAL_USER_IFINDEX, &mtu_len, 0, BPF_MTU_CHK_SEGS); 300 301 return err == -EINVAL ? BPF_OK : BPF_DROP; 302 } 303