test_check_mtu.c (7088B)
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 11char _license[] SEC("license") = "GPL"; 12 13/* Userspace will update with MTU it can see on device */ 14volatile const int GLOBAL_USER_MTU; 15volatile 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 21SEC("xdp") 22int 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 32SEC("xdp") 33int 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 53out: 54 global_bpf_mtu_xdp = mtu_len; 55 return retval; 56} 57 58SEC("xdp") 59int 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 84SEC("xdp") 85int 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 108SEC("xdp") 109int 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 132SEC("xdp") 133int 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 156SEC("tc") 157int 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; 170out: 171 global_bpf_mtu_tc = mtu_len; 172 return retval; 173} 174 175SEC("tc") 176int 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 199SEC("tc") 200int 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 226SEC("tc") 227int 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 248SEC("tc") 249int 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 268SEC("tc") 269int 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}