forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
samples/bpf: Add example of ipv4 and ipv6 forwarding in XDP
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 <[email protected]> Acked-by: David S. Miller <[email protected]> Acked-by: Jesper Dangaard Brouer <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
- Loading branch information
Showing
4 changed files
with
258 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2017-18 David Ahern <[email protected]> | ||
* | ||
* 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 <uapi/linux/bpf.h> | ||
#include <linux/in.h> | ||
#include <linux/if_ether.h> | ||
#include <linux/if_packet.h> | ||
#include <linux/if_vlan.h> | ||
#include <linux/ip.h> | ||
#include <linux/ipv6.h> | ||
|
||
#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"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2017-18 David Ahern <[email protected]> | ||
* | ||
* 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 <linux/bpf.h> | ||
#include <linux/if_link.h> | ||
#include <linux/limits.h> | ||
#include <net/if.h> | ||
#include <errno.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <stdbool.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <fcntl.h> | ||
#include <libgen.h> | ||
|
||
#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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters