162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci * Copyright (c) 2018 Facebook 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or 562306a36Sopenharmony_ci * modify it under the terms of version 2 of the GNU General Public 662306a36Sopenharmony_ci * License as published by the Free Software Foundation. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/bpf.h> 962306a36Sopenharmony_ci#include <linux/if_link.h> 1062306a36Sopenharmony_ci#include <assert.h> 1162306a36Sopenharmony_ci#include <errno.h> 1262306a36Sopenharmony_ci#include <signal.h> 1362306a36Sopenharmony_ci#include <stdio.h> 1462306a36Sopenharmony_ci#include <stdlib.h> 1562306a36Sopenharmony_ci#include <string.h> 1662306a36Sopenharmony_ci#include <net/if.h> 1762306a36Sopenharmony_ci#include <arpa/inet.h> 1862306a36Sopenharmony_ci#include <netinet/ether.h> 1962306a36Sopenharmony_ci#include <unistd.h> 2062306a36Sopenharmony_ci#include <time.h> 2162306a36Sopenharmony_ci#include <bpf/bpf.h> 2262306a36Sopenharmony_ci#include <bpf/libbpf.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define STATS_INTERVAL_S 2U 2562306a36Sopenharmony_ci#define MAX_PCKT_SIZE 600 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int ifindex = -1; 2862306a36Sopenharmony_cistatic __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 2962306a36Sopenharmony_cistatic __u32 prog_id; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void int_exit(int sig) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci __u32 curr_prog_id = 0; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (ifindex > -1) { 3662306a36Sopenharmony_ci if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { 3762306a36Sopenharmony_ci printf("bpf_xdp_query_id failed\n"); 3862306a36Sopenharmony_ci exit(1); 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci if (prog_id == curr_prog_id) 4162306a36Sopenharmony_ci bpf_xdp_detach(ifindex, xdp_flags, NULL); 4262306a36Sopenharmony_ci else if (!curr_prog_id) 4362306a36Sopenharmony_ci printf("couldn't find a prog id on a given iface\n"); 4462306a36Sopenharmony_ci else 4562306a36Sopenharmony_ci printf("program on interface changed, not removing\n"); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci exit(0); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* simple "icmp packet too big sent" counter 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic void poll_stats(unsigned int map_fd, unsigned int kill_after_s) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci time_t started_at = time(NULL); 5562306a36Sopenharmony_ci __u64 value = 0; 5662306a36Sopenharmony_ci int key = 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci while (!kill_after_s || time(NULL) - started_at <= kill_after_s) { 6062306a36Sopenharmony_ci sleep(STATS_INTERVAL_S); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci printf("icmp \"packet too big\" sent: %10llu pkts\n", value); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void usage(const char *cmd) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci printf("Start a XDP prog which send ICMP \"packet too big\" \n" 7162306a36Sopenharmony_ci "messages if ingress packet is bigger then MAX_SIZE bytes\n"); 7262306a36Sopenharmony_ci printf("Usage: %s [...]\n", cmd); 7362306a36Sopenharmony_ci printf(" -i <ifname|ifindex> Interface\n"); 7462306a36Sopenharmony_ci printf(" -T <stop-after-X-seconds> Default: 0 (forever)\n"); 7562306a36Sopenharmony_ci printf(" -P <MAX_PCKT_SIZE> Default: %u\n", MAX_PCKT_SIZE); 7662306a36Sopenharmony_ci printf(" -S use skb-mode\n"); 7762306a36Sopenharmony_ci printf(" -N enforce native mode\n"); 7862306a36Sopenharmony_ci printf(" -F force loading prog\n"); 7962306a36Sopenharmony_ci printf(" -h Display this help\n"); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ciint main(int argc, char **argv) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci unsigned char opt_flags[256] = {}; 8562306a36Sopenharmony_ci const char *optstr = "i:T:P:SNFh"; 8662306a36Sopenharmony_ci struct bpf_prog_info info = {}; 8762306a36Sopenharmony_ci __u32 info_len = sizeof(info); 8862306a36Sopenharmony_ci unsigned int kill_after_s = 0; 8962306a36Sopenharmony_ci int i, prog_fd, map_fd, opt; 9062306a36Sopenharmony_ci struct bpf_program *prog; 9162306a36Sopenharmony_ci struct bpf_object *obj; 9262306a36Sopenharmony_ci __u32 max_pckt_size = 0; 9362306a36Sopenharmony_ci __u32 key = 0; 9462306a36Sopenharmony_ci char filename[256]; 9562306a36Sopenharmony_ci int err; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci for (i = 0; i < strlen(optstr); i++) 9862306a36Sopenharmony_ci if (optstr[i] != 'h' && 'a' <= optstr[i] && optstr[i] <= 'z') 9962306a36Sopenharmony_ci opt_flags[(unsigned char)optstr[i]] = 1; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci while ((opt = getopt(argc, argv, optstr)) != -1) { 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci switch (opt) { 10462306a36Sopenharmony_ci case 'i': 10562306a36Sopenharmony_ci ifindex = if_nametoindex(optarg); 10662306a36Sopenharmony_ci if (!ifindex) 10762306a36Sopenharmony_ci ifindex = atoi(optarg); 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci case 'T': 11062306a36Sopenharmony_ci kill_after_s = atoi(optarg); 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case 'P': 11362306a36Sopenharmony_ci max_pckt_size = atoi(optarg); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case 'S': 11662306a36Sopenharmony_ci xdp_flags |= XDP_FLAGS_SKB_MODE; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci case 'N': 11962306a36Sopenharmony_ci /* default, set below */ 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci case 'F': 12262306a36Sopenharmony_ci xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci default: 12562306a36Sopenharmony_ci usage(argv[0]); 12662306a36Sopenharmony_ci return 1; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci opt_flags[opt] = 0; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) 13262306a36Sopenharmony_ci xdp_flags |= XDP_FLAGS_DRV_MODE; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci for (i = 0; i < strlen(optstr); i++) { 13562306a36Sopenharmony_ci if (opt_flags[(unsigned int)optstr[i]]) { 13662306a36Sopenharmony_ci fprintf(stderr, "Missing argument -%c\n", optstr[i]); 13762306a36Sopenharmony_ci usage(argv[0]); 13862306a36Sopenharmony_ci return 1; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (!ifindex) { 14362306a36Sopenharmony_ci fprintf(stderr, "Invalid ifname\n"); 14462306a36Sopenharmony_ci return 1; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci obj = bpf_object__open_file(filename, NULL); 15062306a36Sopenharmony_ci if (libbpf_get_error(obj)) 15162306a36Sopenharmony_ci return 1; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci prog = bpf_object__next_program(obj, NULL); 15462306a36Sopenharmony_ci bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci err = bpf_object__load(obj); 15762306a36Sopenharmony_ci if (err) 15862306a36Sopenharmony_ci return 1; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci prog_fd = bpf_program__fd(prog); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* static global var 'max_pcktsz' is accessible from .data section */ 16362306a36Sopenharmony_ci if (max_pckt_size) { 16462306a36Sopenharmony_ci map_fd = bpf_object__find_map_fd_by_name(obj, "xdp_adju.data"); 16562306a36Sopenharmony_ci if (map_fd < 0) { 16662306a36Sopenharmony_ci printf("finding a max_pcktsz map in obj file failed\n"); 16762306a36Sopenharmony_ci return 1; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci bpf_map_update_elem(map_fd, &key, &max_pckt_size, BPF_ANY); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* fetch icmpcnt map */ 17362306a36Sopenharmony_ci map_fd = bpf_object__find_map_fd_by_name(obj, "icmpcnt"); 17462306a36Sopenharmony_ci if (map_fd < 0) { 17562306a36Sopenharmony_ci printf("finding a icmpcnt map in obj file failed\n"); 17662306a36Sopenharmony_ci return 1; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci signal(SIGINT, int_exit); 18062306a36Sopenharmony_ci signal(SIGTERM, int_exit); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { 18362306a36Sopenharmony_ci printf("link set xdp fd failed\n"); 18462306a36Sopenharmony_ci return 1; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len); 18862306a36Sopenharmony_ci if (err) { 18962306a36Sopenharmony_ci printf("can't get prog info - %s\n", strerror(errno)); 19062306a36Sopenharmony_ci return 1; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci prog_id = info.id; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci poll_stats(map_fd, kill_after_s); 19562306a36Sopenharmony_ci int_exit(0); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 199