diff options
author | Jesper Dangaard Brouer <brouer@redhat.com> | 2017-01-23 21:48:06 +0100 |
---|---|---|
committer | Jesper Dangaard Brouer <brouer@redhat.com> | 2017-01-25 15:55:56 +0100 |
commit | 23a6a4b2918ee8f293efae11afdf3ccb3431b1bb (patch) | |
tree | bfebf99868ecb7015256eec873d61aea0bdf30a8 | |
parent | 61eb4fe54fe19ff20277a06dea39f65a1302e653 (diff) | |
download | net-next-xdp-xdp01.tar.gz |
samples/bpf: XDP example of parsing TTL value of IP-headerxdp01
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
-rw-r--r-- | samples/bpf/Makefile | 4 | ||||
-rw-r--r-- | samples/bpf/xdp_ttl_kern.c | 156 | ||||
-rw-r--r-- | samples/bpf/xdp_ttl_user.c | 203 |
3 files changed, 363 insertions, 0 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 09e9d535bd7487..e631d8cf5b8b4b 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -34,6 +34,7 @@ hostprogs-y += sampleip hostprogs-y += tc_l2_redirect hostprogs-y += lwt_len_hist hostprogs-y += xdp_tx_iptunnel +hostprogs-y += xdp_ttl # Libbpf dependencies LIBBPF := ../../tools/lib/bpf/bpf.o @@ -72,6 +73,7 @@ sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o +xdp_ttl-objs := bpf_load.o $(LIBBPF) xdp_ttl_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -105,6 +107,7 @@ always += trace_event_kern.o always += sampleip_kern.o always += lwt_len_hist_kern.o always += xdp_tx_iptunnel_kern.o +always += xdp_ttl_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/lib/ @@ -139,6 +142,7 @@ HOSTLOADLIBES_sampleip += -lelf HOSTLOADLIBES_tc_l2_redirect += -l elf HOSTLOADLIBES_lwt_len_hist += -l elf HOSTLOADLIBES_xdp_tx_iptunnel += -lelf +HOSTLOADLIBES_xdp_ttl += -lelf # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang diff --git a/samples/bpf/xdp_ttl_kern.c b/samples/bpf/xdp_ttl_kern.c new file mode 100644 index 00000000000000..68f6bef49e6489 --- /dev/null +++ b/samples/bpf/xdp_ttl_kern.c @@ -0,0 +1,156 @@ +/* XDP example of parsing TTL value of IP-header. + * + * Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc. + */ +#define KBUILD_MODNAME "foo" +#include <uapi/linux/bpf.h> +#include <uapi/linux/if_ether.h> +#include <uapi/linux/if_packet.h> +#include <uapi/linux/if_vlan.h> +#include <uapi/linux/ip.h> +#include <uapi/linux/in.h> +#include <uapi/linux/tcp.h> +#include "bpf_helpers.h" + +struct vlan_hdr { + __be16 h_vlan_TCI; + __be16 h_vlan_encapsulated_proto; +}; + +struct bpf_map_def SEC("maps") ttl_map = { + .type = BPF_MAP_TYPE_PERCPU_ARRAY, + .key_size = sizeof(u32), /* u8 does not work?! */ + .value_size = sizeof(u64), + .max_entries = 256, +}; + +//#define DEBUG 1 +#ifdef DEBUG +/* Only use this for debug output. Notice output from bpf_trace_printk() + * end-up in /sys/kernel/debug/tracing/trace_pipe + */ +#define bpf_debug(fmt, ...) \ + ({ \ + char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), \ + ##__VA_ARGS__); \ + }) +#else +#define bpf_debug(fmt, ...) { } while (0) +#endif + +/* Parse Ethernet layer 2, extract network layer 3 offset and protocol + * + * Returns false on error and non-supported ether-type + */ +static __always_inline +bool parse_eth(struct ethhdr *eth, void *data_end, + u16 *eth_proto, u64 *l3_offset) +{ + u16 eth_type; + u64 offset; + + offset = sizeof(*eth); + if ((void *)eth + offset > data_end) + return false; + + eth_type = eth->h_proto; + bpf_debug("Debug: eth_type:0x%x\n", ntohs(eth_type)); + + /* Skip non 802.3 Ethertypes */ + if (unlikely(ntohs(eth_type) < ETH_P_802_3_MIN)) + return false; + + /* Handle VLAN tagged packet */ + if (eth_type == htons(ETH_P_8021Q) || eth_type == htons(ETH_P_8021AD)) { + struct vlan_hdr *vlan_hdr; + + vlan_hdr = (void *)eth + offset; + offset += sizeof(*vlan_hdr); + if ((void *)eth + offset > data_end) + return false; + eth_type = vlan_hdr->h_vlan_encapsulated_proto; + } + /* TODO: Handle double VLAN tagged packet */ + + *eth_proto = ntohs(eth_type); + *l3_offset = offset; + return true; +} + +static __always_inline +u32 parse_ipv4(struct xdp_md *ctx, u64 l3_offset) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct iphdr *iph = data + l3_offset; + u64 *counter; + u64 total = 0; + u32 ttl; /* type need to match map */ + + if (iph + 1 > data_end) { + bpf_debug("Invalid IPv4 packet: L3off:%llu\n", l3_offset); + return XDP_ABORTED; + } + /* Extract TTL*/ + ttl = iph->ttl; + bpf_debug("Valid IPv4 packet: TTL:%u\n", ttl); + + counter = bpf_map_lookup_elem(&ttl_map, &ttl); + if (counter) { + /* Don't need __sync_fetch_and_add(); as percpu map */ + *counter += 1; + total = *counter; + } + + if (total > 1000000) { + /* Too many packet drop some */ + //if (total & 1) + // return XDP_DROP; + } + + return XDP_PASS; +} + +static __always_inline +u32 handle_eth_protocol(struct xdp_md *ctx, u16 eth_proto, u64 l3_offset) +{ + switch (eth_proto) { + case ETH_P_IP: + return parse_ipv4(ctx, l3_offset); + break; + case ETH_P_IPV6: /* Not handler for IPv6 yet*/ + case ETH_P_ARP: /* Let OS handle ARP */ + /* Fall-through */ + default: + bpf_debug("Not handling eth_proto:0x%x\n", eth_proto); + return XDP_PASS; + } + return XDP_PASS; +} + + +SEC("xdp_ttl") +int xdp_ttl_program(struct xdp_md *ctx) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct ethhdr *eth = data; + u16 eth_proto = 0; + u64 l3_offset = 0; + u32 action; + + if (!(parse_eth(eth, data_end, ð_proto, &l3_offset))) { + bpf_debug("Cannot parse L2: L3off:%llu proto:0x%x\n", + l3_offset, eth_proto); + return XDP_PASS; /* Skip */ + } + + bpf_debug("Reached L3: L3off:%llu proto:0x%x\n", l3_offset, eth_proto); + + /* */ + action = handle_eth_protocol(ctx, eth_proto, l3_offset); + return action; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/xdp_ttl_user.c b/samples/bpf/xdp_ttl_user.c new file mode 100644 index 00000000000000..a87006c03f54ba --- /dev/null +++ b/samples/bpf/xdp_ttl_user.c @@ -0,0 +1,203 @@ +/* Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc. + */ +static const char *__doc__= + " XDP example of parsing TTL value of IP-header."; + +#include <linux/bpf.h> +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> + +#include <sys/resource.h> +#include <getopt.h> + +#include "bpf_load.h" +#include "bpf_util.h" +#include "libbpf.h" + +static int ifindex = -1; + +static void int_exit(int sig) +{ + fprintf(stderr, "Interrupted: Removing XDP program on ifindex:%d\n", + ifindex); + if (ifindex > -1) + set_link_xdp_fd(ifindex, -1); + exit(0); +} + +static const struct option long_options[] = { + {"help", no_argument, NULL, 'h' }, + {"ifindex", required_argument, NULL, 'i' }, + {0, 0, NULL, 0 } +}; + +/* Exit return codes */ +#define EXIT_OK 0 +#define EXIT_FAIL 1 +#define EXIT_FAIL_OPTION 2 +#define EXIT_FAIL_XDP 3 + +static void usage(char *argv[]) +{ + int i; + printf("\nDOCUMENTATION:\n%s\n", __doc__); + printf("\n"); + printf(" Usage: %s (options-see-below)\n", + argv[0]); + printf(" Listing options:\n"); + for (i = 0; long_options[i].name != 0; i++) { + printf(" --%-12s", long_options[i].name); + if (long_options[i].flag != NULL) + printf(" flag (internal value:%d)", + *long_options[i].flag); + else + printf(" short-option: -%c", + long_options[i].val); + printf("\n"); + } + printf("\n"); +} + +#define MAX_KEYS 256 + +struct ttl_stats { + __u64 data[MAX_KEYS]; +}; + +static bool stats_collect(struct ttl_stats *record) +{ + unsigned int nr_cpus = bpf_num_possible_cpus(); + const unsigned int nr_keys = MAX_KEYS; + __u64 values[nr_cpus]; + __u32 key; + int i; + + for (key = 0; key < nr_keys; key++) { + __u64 sum = 0; + + if ((bpf_map_lookup_elem(map_fd[0], &key, values)) != 0) { + printf("DEBUG: bpf_map_lookup_elem failed\n"); + return false; + } + + /* Sum values from each CPU */ + for (i = 0; i < nr_cpus; i++) { + sum += values[i]; + } + + record->data[key] = sum; + } + return true; +} + +static void stats_print_headers(void) +{ + static unsigned int i; +#define DEBUG 1 +#ifdef DEBUG + { + int debug_notice_interval = 3; + char msg[] = + "\nDebug outout avail via:\n" + " sudo cat /sys/kernel/debug/tracing/trace_pipe\n\n"; + printf(msg, debug_notice_interval); + } +#endif + i++; + printf("Stats: %d\n", i); +} + +static void stats_print(struct ttl_stats *record) +{ + const unsigned int nr_keys = MAX_KEYS; + __u64 count; + __u32 ttl; + + /* clear screen */ + printf("\033[2J"); + + stats_print_headers(); + for (ttl = 0; ttl < nr_keys; ttl++) { + count = record->data[ttl]; + if (count) + printf("TTL: %3d count:%llu\n", ttl, count); + } +} + +static void stats_poll(int interval) +{ + struct ttl_stats record; + + while (1) { + memset(&record, 0, sizeof(record)); + + if (stats_collect(&record)) + stats_print(&record); + + sleep(interval); + } +} + +int main(int argc, char **argv) +{ + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + char filename[256]; + int longindex = 0; + int opt; + + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); + + /* Parse commands line args */ + while ((opt = getopt_long(argc, argv, "hi:", + long_options, &longindex)) != -1) { + switch (opt) { + case 'i': + ifindex = atoi(optarg); + break; + case 'h': + default: + usage(argv); + return EXIT_FAIL_OPTION; + } + } + /* Required options */ + if (ifindex == -1) { + printf("**Error**: required option --ifindex missing"); + usage(argv); + return EXIT_FAIL_OPTION; + } + + /* Increase resource limits */ + if (setrlimit(RLIMIT_MEMLOCK, &r)) { + perror("setrlimit(RLIMIT_MEMLOCK, RLIM_INFINITY)"); + return 1; + } + + if (load_bpf_file(filename)) { + printf("%s", bpf_log_buf); + return 1; + } + + if (!prog_fd[0]) { + printf("load_bpf_file: %s\n", strerror(errno)); + return 1; + } + + /* Remove XDP program when program is interrupted */ + signal(SIGINT, int_exit); + + if (set_link_xdp_fd(ifindex, prog_fd[0]) < 0) { + printf("link set xdp fd failed\n"); + return EXIT_FAIL_XDP; + } + + stats_poll(1); + + return EXIT_OK; +} |