From patchwork Thu May 10 03:34:27 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Ahern X-Patchwork-Id: 911266 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Syuq5xDR"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40hJkl0YqLz9s35 for ; Thu, 10 May 2018 13:34:55 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756622AbeEJDew (ORCPT ); Wed, 9 May 2018 23:34:52 -0400 Received: from mail-pl0-f67.google.com ([209.85.160.67]:33741 "EHLO mail-pl0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756593AbeEJDev (ORCPT ); Wed, 9 May 2018 23:34:51 -0400 Received: by mail-pl0-f67.google.com with SMTP id n10-v6so474281plp.0 for ; Wed, 09 May 2018 20:34:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=TZAOKol0NocEbtdM1PaVPl2T1C8TdtYkGWGPWjP820I=; b=Syuq5xDR6Y6NTHaxQGVk1tKzzRP1wBzottR0ZtJo+XzytwK4ARiaZrZo2cGcFv6PvZ j4FcgyGWKFkv2mVI70Mp/YM8AmT7BRDCz/v79R0I65aaPndltQJgs810AuSqNNEpcsFv KAS7R9Y07/fFjjFjlcKvEwfOhW5iagie/w0WOQi6NBajt6sNn3H20rgUmkkqqba4+s/O IkwxOe4L+pWjDbMBYI4s2y3AhNvQphB83rPf5ZjDh+Lc1N5ZgWl5hkTjgTOxD92nXNpC zM8Lp7zKrAZUBT+7vRdWcvbK0KK980Jd24JO9Vfu3leeH3RRQwa4nPZmPRhvZUBRgPbp E7yQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=TZAOKol0NocEbtdM1PaVPl2T1C8TdtYkGWGPWjP820I=; b=P94cn7AjgZP7Of2UPt9DU8aPoaMZ+zzQf8PjiR/1wtnMxQvmk3jc+v9taSqZA4LwS0 unmjdHBcu3cBVIwaWYGWTX4+jKmiEm291XwtM1jX+J+qRnpxUtLwTaog2Qsa0heeON4g 2rRQtEHq9M/Q9WW+1OoHB0XNr6UdLeipUaH4rHlLINcLAkqapKOV1JyHuxcBKwzmdp2+ WIxgHT1tFvWWjWAXXpW9d0oN62ugX1ijp1ziHTiFDvoKzEerKEL6gKAwDvSKjD0KHXXm mebvvTBR8mupJdje8LWIIZEMsOzL/CxF4u7TXMCe0EIAxjUVCO8E/7iGlUPhMVw7IHa6 QE8Q== X-Gm-Message-State: ALQs6tBMedjGnYgJkL4EwVT44A21sAyQO0FbPjWw9ggnJaf/qb/hOlNm v0OZ6wZ+ilj5v6k2maz0/wrgrA== X-Google-Smtp-Source: AB8JxZqP25AGz7BL6/9M8vDJhWJtNwj4T36FmXPXg3TCt2/ywWyyUBOV+vyosBiT3ApSH1m5vF4elQ== X-Received: by 2002:a17:902:5948:: with SMTP id e8-v6mr48741421plj.121.1525923289919; Wed, 09 May 2018 20:34:49 -0700 (PDT) Received: from kenny.it.cumulusnetworks.com. (fw.cumulusnetworks.com. [216.129.126.126]) by smtp.googlemail.com with ESMTPSA id x71sm71288800pfe.47.2018.05.09.20.34.48 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 09 May 2018 20:34:49 -0700 (PDT) From: David Ahern To: netdev@vger.kernel.org, borkmann@iogearbox.net, ast@kernel.org Cc: davem@davemloft.net, shm@cumulusnetworks.com, roopa@cumulusnetworks.com, brouer@redhat.com, toke@toke.dk, john.fastabend@gmail.com, David Ahern Subject: [bpf-next v3 9/9] samples/bpf: Add example of ipv4 and ipv6 forwarding in XDP Date: Wed, 9 May 2018 20:34:27 -0700 Message-Id: <20180510033427.20756-10-dsahern@gmail.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180510033427.20756-1-dsahern@gmail.com> References: <20180510033427.20756-1-dsahern@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Simple example of fast-path forwarding. It has a serious flaw in not verifying the egress device index supports XDP forwarding. If the egress device does not packets are dropped. Take this only as a simple example of fast-path forwarding. Signed-off-by: David Ahern Acked-by: David S. Miller Acked-by: Jesper Dangaard Brouer --- samples/bpf/Makefile | 4 + samples/bpf/xdp_fwd_kern.c | 115 +++++++++++++++++++++++++ samples/bpf/xdp_fwd_user.c | 136 ++++++++++++++++++++++++++++++ tools/testing/selftests/bpf/bpf_helpers.h | 3 + 4 files changed, 258 insertions(+) create mode 100644 samples/bpf/xdp_fwd_kern.c create mode 100644 samples/bpf/xdp_fwd_user.c diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 8e0c7fb6d7cc..28513d6be1bf 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -46,6 +46,7 @@ hostprogs-y += syscall_tp hostprogs-y += cpustat hostprogs-y += xdp_adjust_tail hostprogs-y += xdpsock +hostprogs-y += xdp_fwd # Libbpf dependencies LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o @@ -100,6 +101,7 @@ syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o xdp_adjust_tail-objs := bpf_load.o $(LIBBPF) xdp_adjust_tail_user.o xdpsock-objs := bpf_load.o $(LIBBPF) xdpsock_user.o +xdp_fwd-objs := bpf_load.o $(LIBBPF) xdp_fwd_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -154,6 +156,7 @@ always += syscall_tp_kern.o always += cpustat_kern.o always += xdp_adjust_tail_kern.o always += xdpsock_kern.o +always += xdp_fwd_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/lib/ @@ -201,6 +204,7 @@ HOSTLOADLIBES_syscall_tp += -lelf HOSTLOADLIBES_cpustat += -lelf HOSTLOADLIBES_xdp_adjust_tail += -lelf HOSTLOADLIBES_xdpsock += -lelf -pthread +HOSTLOADLIBES_xdp_fwd += -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_fwd_kern.c b/samples/bpf/xdp_fwd_kern.c new file mode 100644 index 000000000000..cdf4fc383cc9 --- /dev/null +++ b/samples/bpf/xdp_fwd_kern.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2017-18 David Ahern + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#define KBUILD_MODNAME "foo" +#include +#include +#include +#include +#include +#include +#include + +#include "bpf_helpers.h" + +#define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF) + +struct bpf_map_def SEC("maps") tx_port = { + .type = BPF_MAP_TYPE_DEVMAP, + .key_size = sizeof(int), + .value_size = sizeof(int), + .max_entries = 64, +}; + +static __always_inline int xdp_fwd_flags(struct xdp_md *ctx, u32 flags) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct bpf_fib_lookup fib_params; + struct ethhdr *eth = data; + int out_index; + u16 h_proto; + u64 nh_off; + + nh_off = sizeof(*eth); + if (data + nh_off > data_end) + return XDP_DROP; + + __builtin_memset(&fib_params, 0, sizeof(fib_params)); + + h_proto = eth->h_proto; + if (h_proto == htons(ETH_P_IP)) { + struct iphdr *iph = data + nh_off; + + if (iph + 1 > data_end) + return XDP_DROP; + + fib_params.family = AF_INET; + fib_params.tos = iph->tos; + fib_params.l4_protocol = iph->protocol; + fib_params.sport = 0; + fib_params.dport = 0; + fib_params.tot_len = ntohs(iph->tot_len); + fib_params.ipv4_src = iph->saddr; + fib_params.ipv4_dst = iph->daddr; + } else if (h_proto == htons(ETH_P_IPV6)) { + struct in6_addr *src = (struct in6_addr *) fib_params.ipv6_src; + struct in6_addr *dst = (struct in6_addr *) fib_params.ipv6_dst; + struct ipv6hdr *iph = data + nh_off; + + if (iph + 1 > data_end) + return XDP_DROP; + + fib_params.family = AF_INET6; + fib_params.flowlabel = *(__be32 *)iph & IPV6_FLOWINFO_MASK; + fib_params.l4_protocol = iph->nexthdr; + fib_params.sport = 0; + fib_params.dport = 0; + fib_params.tot_len = ntohs(iph->payload_len); + *src = iph->saddr; + *dst = iph->daddr; + } else { + return XDP_PASS; + } + + fib_params.ifindex = ctx->ingress_ifindex; + + out_index = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), flags); + + /* verify egress index has xdp support + * TO-DO bpf_map_lookup_elem(&tx_port, &key) fails with + * cannot pass map_type 14 into func bpf_map_lookup_elem#1: + * NOTE: without verification that egress index supports XDP + * forwarding packets are dropped. + */ + if (out_index > 0) { + memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN); + memcpy(eth->h_source, fib_params.smac, ETH_ALEN); + return bpf_redirect_map(&tx_port, out_index, 0); + } + + return XDP_PASS; +} + +SEC("xdp_fwd") +int xdp_fwd_prog(struct xdp_md *ctx) +{ + return xdp_fwd_flags(ctx, 0); +} + +SEC("xdp_fwd_direct") +int xdp_fwd_direct_prog(struct xdp_md *ctx) +{ + return xdp_fwd_flags(ctx, BPF_FIB_LOOKUP_DIRECT); +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/xdp_fwd_user.c b/samples/bpf/xdp_fwd_user.c new file mode 100644 index 000000000000..9c6606f57126 --- /dev/null +++ b/samples/bpf/xdp_fwd_user.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2017-18 David Ahern + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bpf_load.h" +#include "bpf_util.h" +#include "libbpf.h" + + +static int do_attach(int idx, int fd, const char *name) +{ + int err; + + err = bpf_set_link_xdp_fd(idx, fd, 0); + if (err < 0) + printf("ERROR: failed to attach program to %s\n", name); + + return err; +} + +static int do_detach(int idx, const char *name) +{ + int err; + + err = bpf_set_link_xdp_fd(idx, -1, 0); + if (err < 0) + printf("ERROR: failed to detach program from %s\n", name); + + return err; +} + +static void usage(const char *prog) +{ + fprintf(stderr, + "usage: %s [OPTS] interface-list\n" + "\nOPTS:\n" + " -d detach program\n" + " -D direct table lookups (skip fib rules)\n", + prog); +} + +int main(int argc, char **argv) +{ + char filename[PATH_MAX]; + int opt, i, idx, err; + int prog_id = 0; + int attach = 1; + int ret = 0; + + while ((opt = getopt(argc, argv, ":dD")) != -1) { + switch (opt) { + case 'd': + attach = 0; + break; + case 'D': + prog_id = 1; + break; + default: + usage(basename(argv[0])); + return 1; + } + } + + if (optind == argc) { + usage(basename(argv[0])); + return 1; + } + + if (attach) { + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); + + if (access(filename, O_RDONLY) < 0) { + printf("error accessing file %s: %s\n", + filename, strerror(errno)); + return 1; + } + + if (load_bpf_file(filename)) { + printf("%s", bpf_log_buf); + return 1; + } + + if (!prog_fd[prog_id]) { + printf("load_bpf_file: %s\n", strerror(errno)); + return 1; + } + } + if (attach) { + for (i = 1; i < 64; ++i) + bpf_map_update_elem(map_fd[0], &i, &i, 0); + } + + for (i = optind; i < argc; ++i) { + idx = if_nametoindex(argv[i]); + if (!idx) + idx = strtoul(argv[i], NULL, 0); + + if (!idx) { + fprintf(stderr, "Invalid arg\n"); + return 1; + } + if (!attach) { + err = do_detach(idx, argv[i]); + if (err) + ret = err; + } else { + err = do_attach(idx, prog_fd[prog_id], argv[i]); + if (err) + ret = err; + } + } + + return ret; +} diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 265f8e0e8ada..2375d06c706b 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -103,6 +103,9 @@ static int (*bpf_skb_get_xfrm_state)(void *ctx, int index, void *state, (void *) BPF_FUNC_skb_get_xfrm_state; static int (*bpf_get_stack)(void *ctx, void *buf, int size, int flags) = (void *) BPF_FUNC_get_stack; +static int (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, + int plen, __u32 flags) = + (void *) BPF_FUNC_fib_lookup; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions