linux/tools/testing/selftests/bpf/progs/test_check_mtu.c
<<
>>
Prefs
   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("classifier")
 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("classifier")
 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("classifier")
 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("classifier")
 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("classifier")
 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("classifier")
 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