291 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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 helpers99 * are responsible for catching this, when doing actual size adjust100 */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 helpers239 * are responsible for catching this, when doing actual size adjust240 */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}291