linux/samples/bpf/xdp_adjust_tail_user.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0
   2 * Copyright (c) 2018 Facebook
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of version 2 of the GNU General Public
   6 * License as published by the Free Software Foundation.
   7 */
   8#include <linux/bpf.h>
   9#include <linux/if_link.h>
  10#include <assert.h>
  11#include <errno.h>
  12#include <signal.h>
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <string.h>
  16#include <net/if.h>
  17#include <sys/resource.h>
  18#include <arpa/inet.h>
  19#include <netinet/ether.h>
  20#include <unistd.h>
  21#include <time.h>
  22#include <bpf/bpf.h>
  23#include <bpf/libbpf.h>
  24
  25#define STATS_INTERVAL_S 2U
  26#define MAX_PCKT_SIZE 600
  27
  28static int ifindex = -1;
  29static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
  30static __u32 prog_id;
  31
  32static void int_exit(int sig)
  33{
  34        __u32 curr_prog_id = 0;
  35
  36        if (ifindex > -1) {
  37                if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) {
  38                        printf("bpf_get_link_xdp_id failed\n");
  39                        exit(1);
  40                }
  41                if (prog_id == curr_prog_id)
  42                        bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
  43                else if (!curr_prog_id)
  44                        printf("couldn't find a prog id on a given iface\n");
  45                else
  46                        printf("program on interface changed, not removing\n");
  47        }
  48        exit(0);
  49}
  50
  51/* simple "icmp packet too big sent" counter
  52 */
  53static void poll_stats(unsigned int map_fd, unsigned int kill_after_s)
  54{
  55        time_t started_at = time(NULL);
  56        __u64 value = 0;
  57        int key = 0;
  58
  59
  60        while (!kill_after_s || time(NULL) - started_at <= kill_after_s) {
  61                sleep(STATS_INTERVAL_S);
  62
  63                assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
  64
  65                printf("icmp \"packet too big\" sent: %10llu pkts\n", value);
  66        }
  67}
  68
  69static void usage(const char *cmd)
  70{
  71        printf("Start a XDP prog which send ICMP \"packet too big\" \n"
  72                "messages if ingress packet is bigger then MAX_SIZE bytes\n");
  73        printf("Usage: %s [...]\n", cmd);
  74        printf("    -i <ifname|ifindex> Interface\n");
  75        printf("    -T <stop-after-X-seconds> Default: 0 (forever)\n");
  76        printf("    -P <MAX_PCKT_SIZE> Default: %u\n", MAX_PCKT_SIZE);
  77        printf("    -S use skb-mode\n");
  78        printf("    -N enforce native mode\n");
  79        printf("    -F force loading prog\n");
  80        printf("    -h Display this help\n");
  81}
  82
  83int main(int argc, char **argv)
  84{
  85        struct bpf_prog_load_attr prog_load_attr = {
  86                .prog_type      = BPF_PROG_TYPE_XDP,
  87        };
  88        unsigned char opt_flags[256] = {};
  89        const char *optstr = "i:T:P:SNFh";
  90        struct bpf_prog_info info = {};
  91        __u32 info_len = sizeof(info);
  92        unsigned int kill_after_s = 0;
  93        int i, prog_fd, map_fd, opt;
  94        struct bpf_object *obj;
  95        __u32 max_pckt_size = 0;
  96        __u32 key = 0;
  97        char filename[256];
  98        int err;
  99
 100        for (i = 0; i < strlen(optstr); i++)
 101                if (optstr[i] != 'h' && 'a' <= optstr[i] && optstr[i] <= 'z')
 102                        opt_flags[(unsigned char)optstr[i]] = 1;
 103
 104        while ((opt = getopt(argc, argv, optstr)) != -1) {
 105
 106                switch (opt) {
 107                case 'i':
 108                        ifindex = if_nametoindex(optarg);
 109                        if (!ifindex)
 110                                ifindex = atoi(optarg);
 111                        break;
 112                case 'T':
 113                        kill_after_s = atoi(optarg);
 114                        break;
 115                case 'P':
 116                        max_pckt_size = atoi(optarg);
 117                        break;
 118                case 'S':
 119                        xdp_flags |= XDP_FLAGS_SKB_MODE;
 120                        break;
 121                case 'N':
 122                        /* default, set below */
 123                        break;
 124                case 'F':
 125                        xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 126                        break;
 127                default:
 128                        usage(argv[0]);
 129                        return 1;
 130                }
 131                opt_flags[opt] = 0;
 132        }
 133
 134        if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
 135                xdp_flags |= XDP_FLAGS_DRV_MODE;
 136
 137        for (i = 0; i < strlen(optstr); i++) {
 138                if (opt_flags[(unsigned int)optstr[i]]) {
 139                        fprintf(stderr, "Missing argument -%c\n", optstr[i]);
 140                        usage(argv[0]);
 141                        return 1;
 142                }
 143        }
 144
 145        if (!ifindex) {
 146                fprintf(stderr, "Invalid ifname\n");
 147                return 1;
 148        }
 149
 150        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 151        prog_load_attr.file = filename;
 152
 153        if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 154                return 1;
 155
 156        /* static global var 'max_pcktsz' is accessible from .data section */
 157        if (max_pckt_size) {
 158                map_fd = bpf_object__find_map_fd_by_name(obj, "xdp_adju.data");
 159                if (map_fd < 0) {
 160                        printf("finding a max_pcktsz map in obj file failed\n");
 161                        return 1;
 162                }
 163                bpf_map_update_elem(map_fd, &key, &max_pckt_size, BPF_ANY);
 164        }
 165
 166        /* fetch icmpcnt map */
 167        map_fd = bpf_object__find_map_fd_by_name(obj, "icmpcnt");
 168        if (map_fd < 0) {
 169                printf("finding a icmpcnt map in obj file failed\n");
 170                return 1;
 171        }
 172
 173        signal(SIGINT, int_exit);
 174        signal(SIGTERM, int_exit);
 175
 176        if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
 177                printf("link set xdp fd failed\n");
 178                return 1;
 179        }
 180
 181        err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
 182        if (err) {
 183                printf("can't get prog info - %s\n", strerror(errno));
 184                return 1;
 185        }
 186        prog_id = info.id;
 187
 188        poll_stats(map_fd, kill_after_s);
 189        int_exit(0);
 190
 191        return 0;
 192}
 193