Skip to content

Commit 6e66fbb

Browse files
liuhangbinborkmann
authored andcommitted
samples/bpf: Add xdp program on egress for xdp_redirect_map
This patch add a xdp program on egress to show that we can modify the packet on egress. In this sample we will set the pkt's src mac to egress's mac address. The xdp_prog will be attached when -X option supplied. Signed-off-by: Hangbin Liu <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Jesper Dangaard Brouer <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 18b24d7 commit 6e66fbb

File tree

2 files changed

+147
-25
lines changed

2 files changed

+147
-25
lines changed

samples/bpf/xdp_redirect_map_kern.c

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,22 @@
1919
#include <linux/ipv6.h>
2020
#include <bpf/bpf_helpers.h>
2121

22+
/* The 2nd xdp prog on egress does not support skb mode, so we define two
23+
* maps, tx_port_general and tx_port_native.
24+
*/
2225
struct {
2326
__uint(type, BPF_MAP_TYPE_DEVMAP);
2427
__uint(key_size, sizeof(int));
2528
__uint(value_size, sizeof(int));
2629
__uint(max_entries, 100);
27-
} tx_port SEC(".maps");
30+
} tx_port_general SEC(".maps");
31+
32+
struct {
33+
__uint(type, BPF_MAP_TYPE_DEVMAP);
34+
__uint(key_size, sizeof(int));
35+
__uint(value_size, sizeof(struct bpf_devmap_val));
36+
__uint(max_entries, 100);
37+
} tx_port_native SEC(".maps");
2838

2939
/* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
3040
* feedback. Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
3646
__uint(max_entries, 1);
3747
} rxcnt SEC(".maps");
3848

49+
/* map to store egress interface mac address */
50+
struct {
51+
__uint(type, BPF_MAP_TYPE_ARRAY);
52+
__type(key, u32);
53+
__type(value, __be64);
54+
__uint(max_entries, 1);
55+
} tx_mac SEC(".maps");
56+
3957
static void swap_src_dst_mac(void *data)
4058
{
4159
unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
5270
p[5] = dst[2];
5371
}
5472

55-
SEC("xdp_redirect_map")
56-
int xdp_redirect_map_prog(struct xdp_md *ctx)
73+
static __always_inline int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
5774
{
5875
void *data_end = (void *)(long)ctx->data_end;
5976
void *data = (void *)(long)ctx->data;
6077
struct ethhdr *eth = data;
6178
int rc = XDP_DROP;
62-
int vport, port = 0, m = 0;
6379
long *value;
6480
u32 key = 0;
6581
u64 nh_off;
82+
int vport;
6683

6784
nh_off = sizeof(*eth);
6885
if (data + nh_off > data_end)
@@ -79,7 +96,40 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
7996
swap_src_dst_mac(data);
8097

8198
/* send packet out physical port */
82-
return bpf_redirect_map(&tx_port, vport, 0);
99+
return bpf_redirect_map(redirect_map, vport, 0);
100+
}
101+
102+
SEC("xdp_redirect_general")
103+
int xdp_redirect_map_general(struct xdp_md *ctx)
104+
{
105+
return xdp_redirect_map(ctx, &tx_port_general);
106+
}
107+
108+
SEC("xdp_redirect_native")
109+
int xdp_redirect_map_native(struct xdp_md *ctx)
110+
{
111+
return xdp_redirect_map(ctx, &tx_port_native);
112+
}
113+
114+
SEC("xdp_devmap/map_prog")
115+
int xdp_redirect_map_egress(struct xdp_md *ctx)
116+
{
117+
void *data_end = (void *)(long)ctx->data_end;
118+
void *data = (void *)(long)ctx->data;
119+
struct ethhdr *eth = data;
120+
__be64 *mac;
121+
u32 key = 0;
122+
u64 nh_off;
123+
124+
nh_off = sizeof(*eth);
125+
if (data + nh_off > data_end)
126+
return XDP_DROP;
127+
128+
mac = bpf_map_lookup_elem(&tx_mac, &key);
129+
if (mac)
130+
__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
131+
132+
return XDP_PASS;
83133
}
84134

85135
/* Redirect require an XDP bpf_prog loaded on the TX device */

samples/bpf/xdp_redirect_map_user.c

Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
#include <unistd.h>
1515
#include <libgen.h>
1616
#include <sys/resource.h>
17+
#include <sys/ioctl.h>
18+
#include <sys/types.h>
19+
#include <sys/socket.h>
20+
#include <netinet/in.h>
1721

1822
#include "bpf_util.h"
1923
#include <bpf/bpf.h>
@@ -22,6 +26,7 @@
2226
static int ifindex_in;
2327
static int ifindex_out;
2428
static bool ifindex_out_xdp_dummy_attached = true;
29+
static bool xdp_devmap_attached;
2530
static __u32 prog_id;
2631
static __u32 dummy_prog_id;
2732

@@ -83,31 +88,59 @@ static void poll_stats(int interval, int ifindex)
8388
}
8489
}
8590

91+
static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
92+
{
93+
char ifname[IF_NAMESIZE];
94+
struct ifreq ifr;
95+
int fd, ret = -1;
96+
97+
fd = socket(AF_INET, SOCK_DGRAM, 0);
98+
if (fd < 0)
99+
return ret;
100+
101+
if (!if_indextoname(ifindex_out, ifname))
102+
goto err_out;
103+
104+
strcpy(ifr.ifr_name, ifname);
105+
106+
if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
107+
goto err_out;
108+
109+
memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
110+
ret = 0;
111+
112+
err_out:
113+
close(fd);
114+
return ret;
115+
}
116+
86117
static void usage(const char *prog)
87118
{
88119
fprintf(stderr,
89120
"usage: %s [OPTS] <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n\n"
90121
"OPTS:\n"
91122
" -S use skb-mode\n"
92123
" -N enforce native mode\n"
93-
" -F force loading prog\n",
124+
" -F force loading prog\n"
125+
" -X load xdp program on egress\n",
94126
prog);
95127
}
96128

97129
int main(int argc, char **argv)
98130
{
99131
struct bpf_prog_load_attr prog_load_attr = {
100-
.prog_type = BPF_PROG_TYPE_XDP,
132+
.prog_type = BPF_PROG_TYPE_UNSPEC,
101133
};
102-
struct bpf_program *prog, *dummy_prog;
134+
struct bpf_program *prog, *dummy_prog, *devmap_prog;
135+
int prog_fd, dummy_prog_fd, devmap_prog_fd = 0;
136+
int tx_port_map_fd, tx_mac_map_fd;
137+
struct bpf_devmap_val devmap_val;
103138
struct bpf_prog_info info = {};
104139
__u32 info_len = sizeof(info);
105-
int prog_fd, dummy_prog_fd;
106-
const char *optstr = "FSN";
140+
const char *optstr = "FSNX";
107141
struct bpf_object *obj;
108142
int ret, opt, key = 0;
109143
char filename[256];
110-
int tx_port_map_fd;
111144

112145
while ((opt = getopt(argc, argv, optstr)) != -1) {
113146
switch (opt) {
@@ -120,14 +153,21 @@ int main(int argc, char **argv)
120153
case 'F':
121154
xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
122155
break;
156+
case 'X':
157+
xdp_devmap_attached = true;
158+
break;
123159
default:
124160
usage(basename(argv[0]));
125161
return 1;
126162
}
127163
}
128164

129-
if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
165+
if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
130166
xdp_flags |= XDP_FLAGS_DRV_MODE;
167+
} else if (xdp_devmap_attached) {
168+
printf("Load xdp program on egress with SKB mode not supported yet\n");
169+
return 1;
170+
}
131171

132172
if (optind == argc) {
133173
printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
@@ -150,24 +190,28 @@ int main(int argc, char **argv)
150190
if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
151191
return 1;
152192

153-
prog = bpf_program__next(NULL, obj);
154-
dummy_prog = bpf_program__next(prog, obj);
155-
if (!prog || !dummy_prog) {
156-
printf("finding a prog in obj file failed\n");
157-
return 1;
193+
if (xdp_flags & XDP_FLAGS_SKB_MODE) {
194+
prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
195+
tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
196+
} else {
197+
prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
198+
tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
199+
}
200+
dummy_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_dummy_prog");
201+
if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
202+
printf("finding prog/dummy_prog/tx_port_map in obj file failed\n");
203+
goto out;
158204
}
159-
/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
160-
* so we're missing only the fd for dummy prog
161-
*/
205+
prog_fd = bpf_program__fd(prog);
162206
dummy_prog_fd = bpf_program__fd(dummy_prog);
163-
if (prog_fd < 0 || dummy_prog_fd < 0) {
207+
if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
164208
printf("bpf_prog_load_xattr: %s\n", strerror(errno));
165209
return 1;
166210
}
167211

168-
tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
212+
tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
169213
rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
170-
if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
214+
if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
171215
printf("bpf_object__find_map_fd_by_name failed\n");
172216
return 1;
173217
}
@@ -199,11 +243,39 @@ int main(int argc, char **argv)
199243
}
200244
dummy_prog_id = info.id;
201245

246+
/* Load 2nd xdp prog on egress. */
247+
if (xdp_devmap_attached) {
248+
unsigned char mac_addr[6];
249+
250+
devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress");
251+
if (!devmap_prog) {
252+
printf("finding devmap_prog in obj file failed\n");
253+
goto out;
254+
}
255+
devmap_prog_fd = bpf_program__fd(devmap_prog);
256+
if (devmap_prog_fd < 0) {
257+
printf("finding devmap_prog fd failed\n");
258+
goto out;
259+
}
260+
261+
if (get_mac_addr(ifindex_out, mac_addr) < 0) {
262+
printf("get interface %d mac failed\n", ifindex_out);
263+
goto out;
264+
}
265+
266+
ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
267+
if (ret) {
268+
perror("bpf_update_elem tx_mac_map_fd");
269+
goto out;
270+
}
271+
}
272+
202273
signal(SIGINT, int_exit);
203274
signal(SIGTERM, int_exit);
204275

205-
/* populate virtual to physical port map */
206-
ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
276+
devmap_val.ifindex = ifindex_out;
277+
devmap_val.bpf_prog.fd = devmap_prog_fd;
278+
ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
207279
if (ret) {
208280
perror("bpf_update_elem");
209281
goto out;

0 commit comments

Comments
 (0)