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 volatile const int GLOBAL_USER_MTU; 15 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("xdp") 109 int xdp_input_len(struct xdp_md *ctx) 110 { 111 int retval = XDP_PASS; /* Expected retval on successful test */ 112 void *data_end = (void *)(long)ctx->data_end; 113 void *data = (void *)(long)ctx->data; 114 __u32 ifindex = GLOBAL_USER_IFINDEX; 115 __u32 data_len = data_end - data; 116 117 /* API allow user give length to check as input via mtu_len param, 118 * resulting MTU value is still output in mtu_len param after call. 119 * 120 * Input len is L3, like MTU and iph->tot_len. 121 * Remember XDP data_len is L2. 122 */ 123 __u32 mtu_len = data_len - ETH_HLEN; 124 125 if (bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0)) 126 retval = XDP_ABORTED; 127 128 global_bpf_mtu_xdp = mtu_len; 129 return retval; 130 } 131 132 SEC("xdp") 133 int xdp_input_len_exceed(struct xdp_md *ctx) 134 { 135 int retval = XDP_ABORTED; /* Fail */ 136 __u32 ifindex = GLOBAL_USER_IFINDEX; 137 int err; 138 139 /* API allow user give length to check as input via mtu_len param, 140 * resulting MTU value is still output in mtu_len param after call. 141 * 142 * Input length value is L3 size like MTU. 143 */ 144 __u32 mtu_len = GLOBAL_USER_MTU; 145 146 mtu_len += 1; /* Exceed with 1 */ 147 148 err = bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0); 149 if (err == BPF_MTU_CHK_RET_FRAG_NEEDED) 150 retval = XDP_PASS ; /* Success in exceeding MTU check */ 151 152 global_bpf_mtu_xdp = mtu_len; 153 return retval; 154 } 155 156 SEC("classifier") 157 int tc_use_helper(struct __sk_buff *ctx) 158 { 159 int retval = BPF_OK; /* Expected retval on successful test */ 160 __u32 mtu_len = 0; 161 int delta = 0; 162 163 if (bpf_check_mtu(ctx, 0, &mtu_len, delta, 0)) { 164 retval = BPF_DROP; 165 goto out; 166 } 167 168 if (mtu_len != GLOBAL_USER_MTU) 169 retval = BPF_REDIRECT; 170 out: 171 global_bpf_mtu_tc = mtu_len; 172 return retval; 173 } 174 175 SEC("classifier") 176 int tc_exceed_mtu(struct __sk_buff *ctx) 177 { 178 __u32 ifindex = GLOBAL_USER_IFINDEX; 179 int retval = BPF_DROP; /* Fail */ 180 __u32 skb_len = ctx->len; 181 __u32 mtu_len = 0; 182 int delta; 183 int err; 184 185 /* Exceed MTU with 1 via delta adjust */ 186 delta = GLOBAL_USER_MTU - (skb_len - ETH_HLEN) + 1; 187 188 err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); 189 if (err) { 190 retval = BPF_OK; /* Success in exceeding MTU check */ 191 if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) 192 retval = BPF_DROP; 193 } 194 195 global_bpf_mtu_tc = mtu_len; 196 return retval; 197 } 198 199 SEC("classifier") 200 int tc_exceed_mtu_da(struct __sk_buff *ctx) 201 { 202 /* SKB Direct-Access variant */ 203 void *data_end = (void *)(long)ctx->data_end; 204 void *data = (void *)(long)ctx->data; 205 __u32 ifindex = GLOBAL_USER_IFINDEX; 206 __u32 data_len = data_end - data; 207 int retval = BPF_DROP; /* Fail */ 208 __u32 mtu_len = 0; 209 int delta; 210 int err; 211 212 /* Exceed MTU with 1 via delta adjust */ 213 delta = GLOBAL_USER_MTU - (data_len - ETH_HLEN) + 1; 214 215 err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); 216 if (err) { 217 retval = BPF_OK; /* Success in exceeding MTU check */ 218 if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) 219 retval = BPF_DROP; 220 } 221 222 global_bpf_mtu_tc = mtu_len; 223 return retval; 224 } 225 226 SEC("classifier") 227 int tc_minus_delta(struct __sk_buff *ctx) 228 { 229 int retval = BPF_OK; /* Expected retval on successful test */ 230 __u32 ifindex = GLOBAL_USER_IFINDEX; 231 __u32 skb_len = ctx->len; 232 __u32 mtu_len = 0; 233 int delta; 234 235 /* Borderline test case: Minus delta exceeding packet length allowed */ 236 delta = -((skb_len - ETH_HLEN) + 1); 237 238 /* Minus length (adjusted via delta) still pass MTU check, other helpers 239 * are responsible for catching this, when doing actual size adjust 240 */ 241 if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) 242 retval = BPF_DROP; 243 244 global_bpf_mtu_xdp = mtu_len; 245 return retval; 246 } 247 248 SEC("classifier") 249 int tc_input_len(struct __sk_buff *ctx) 250 { 251 int retval = BPF_OK; /* Expected retval on successful test */ 252 __u32 ifindex = GLOBAL_USER_IFINDEX; 253 254 /* API allow user give length to check as input via mtu_len param, 255 * resulting MTU value is still output in mtu_len param after call. 256 * 257 * Input length value is L3 size. 258 */ 259 __u32 mtu_len = GLOBAL_USER_MTU; 260 261 if (bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0)) 262 retval = BPF_DROP; 263 264 global_bpf_mtu_xdp = mtu_len; 265 return retval; 266 } 267 268 SEC("classifier") 269 int tc_input_len_exceed(struct __sk_buff *ctx) 270 { 271 int retval = BPF_DROP; /* Fail */ 272 __u32 ifindex = GLOBAL_USER_IFINDEX; 273 int err; 274 275 /* API allow user give length to check as input via mtu_len param, 276 * resulting MTU value is still output in mtu_len param after call. 277 * 278 * Input length value is L3 size like MTU. 279 */ 280 __u32 mtu_len = GLOBAL_USER_MTU; 281 282 mtu_len += 1; /* Exceed with 1 */ 283 284 err = bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0); 285 if (err == BPF_MTU_CHK_RET_FRAG_NEEDED) 286 retval = BPF_OK; /* Success in exceeding MTU check */ 287 288 global_bpf_mtu_xdp = mtu_len; 289 return retval; 290 } 291