forked from atoonk/xdp-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxdp_pass_user.c
177 lines (146 loc) · 4.75 KB
/
xdp_pass_user.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* SPDX-License-Identifier: GPL-2.0 */
static const char *__doc__ = "Simple XDP prog doing XDP_PASS\n";
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <net/if.h>
#include <linux/if_link.h> /* depend on kernel-headers installed */
#include "../common/common_params.h"
static const struct option_wrapper long_options[] = {
{{"help", no_argument, NULL, 'h' },
"Show help", false},
{{"dev", required_argument, NULL, 'd' },
"Operate on device <ifname>", "<ifname>", true},
{{"skb-mode", no_argument, NULL, 'S' },
"Install XDP program in SKB (AKA generic) mode"},
{{"native-mode", no_argument, NULL, 'N' },
"Install XDP program in native mode"},
{{"auto-mode", no_argument, NULL, 'A' },
"Auto-detect SKB or native mode"},
{{"force", no_argument, NULL, 'F' },
"Force install, replacing existing program on interface"},
{{"unload", no_argument, NULL, 'U' },
"Unload XDP program instead of loading"},
{{0, 0, NULL, 0 }, NULL, false}
};
int load_bpf_object_file__simple(const char *filename)
{
int first_prog_fd = -1;
struct bpf_object *obj;
int err;
/* Use libbpf for extracting BPF byte-code from BPF-ELF object, and
* loading this into the kernel via bpf-syscall
*/
err = bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &first_prog_fd);
if (err) {
fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n",
filename, err, strerror(-err));
return -1;
}
/* Simply return the first program file descriptor.
* (Hint: This will get more advanced later)
*/
return first_prog_fd;
}
static int xdp_link_detach(int ifindex, __u32 xdp_flags)
{
/* Next assignment this will move into ../common/
* (in more generic version)
*/
int err;
if ((err = bpf_set_link_xdp_fd(ifindex, -1, xdp_flags)) < 0) {
fprintf(stderr, "ERR: link set xdp unload failed (err=%d):%s\n",
err, strerror(-err));
return EXIT_FAIL_XDP;
}
return EXIT_OK;
}
int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
{
/* Next assignment this will move into ../common/ */
int err;
/* libbpf provide the XDP net_device link-level hook attach helper */
err = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags);
if (err == -EEXIST && !(xdp_flags & XDP_FLAGS_UPDATE_IF_NOEXIST)) {
/* Force mode didn't work, probably because a program of the
* opposite type is loaded. Let's unload that and try loading
* again.
*/
__u32 old_flags = xdp_flags;
xdp_flags &= ~XDP_FLAGS_MODES;
xdp_flags |= (old_flags & XDP_FLAGS_SKB_MODE) ? XDP_FLAGS_DRV_MODE : XDP_FLAGS_SKB_MODE;
err = bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
if (!err)
err = bpf_set_link_xdp_fd(ifindex, prog_fd, old_flags);
}
if (err < 0) {
fprintf(stderr, "ERR: "
"ifindex(%d) link set xdp fd failed (%d): %s\n",
ifindex, -err, strerror(-err));
switch (-err) {
case EBUSY:
case EEXIST:
fprintf(stderr, "Hint: XDP already loaded on device"
" use --force to swap/replace\n");
break;
case EOPNOTSUPP:
fprintf(stderr, "Hint: Native-XDP not supported"
" use --skb-mode or --auto-mode\n");
break;
default:
break;
}
return EXIT_FAIL_XDP;
}
return EXIT_OK;
}
int main(int argc, char **argv)
{
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
char filename[256] = "xdp_pass_kern.o";
int prog_fd, err;
struct config cfg = {
.xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE,
.ifindex = -1,
.do_unload = false,
};
parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
/* Required option */
if (cfg.ifindex == -1) {
fprintf(stderr, "ERR: required option --dev missing\n");
usage(argv[0], __doc__, long_options, (argc == 1));
return EXIT_FAIL_OPTION;
}
if (cfg.do_unload)
return xdp_link_detach(cfg.ifindex, cfg.xdp_flags);
/* Load the BPF-ELF object file and get back first BPF_prog FD */
prog_fd = load_bpf_object_file__simple(filename);
if (prog_fd <= 0) {
fprintf(stderr, "ERR: loading file: %s\n", filename);
return EXIT_FAIL_BPF;
}
/* At this point: BPF-prog is (only) loaded by the kernel, and prog_fd
* is our file-descriptor handle. Next step is attaching this FD to a
* kernel hook point, in this case XDP net_device link-level hook.
* Fortunately libbpf have a helper for this:
*/
err = xdp_link_attach(cfg.ifindex, cfg.xdp_flags, prog_fd);
if (err)
return err;
/* This step is not really needed , BPF-info via bpf-syscall */
err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
if (err) {
fprintf(stderr, "ERR: can't get prog info - %s\n",
strerror(errno));
return err;
}
printf("Success: Loading "
"XDP prog name:%s(id:%d) on device:%s(ifindex:%d)\n",
info.name, info.id, cfg.ifname, cfg.ifindex);
return EXIT_OK;
}