aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesper Dangaard Brouer <brouer@redhat.com>2017-01-23 21:48:06 +0100
committerJesper Dangaard Brouer <brouer@redhat.com>2017-01-25 15:55:56 +0100
commit23a6a4b2918ee8f293efae11afdf3ccb3431b1bb (patch)
treebfebf99868ecb7015256eec873d61aea0bdf30a8
parent61eb4fe54fe19ff20277a06dea39f65a1302e653 (diff)
downloadnet-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/Makefile4
-rw-r--r--samples/bpf/xdp_ttl_kern.c156
-rw-r--r--samples/bpf/xdp_ttl_user.c203
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, &eth_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;
+}