-
Notifications
You must be signed in to change notification settings - Fork 100
/
Copy pathdhcp_user_xdp.c
198 lines (175 loc) · 4.45 KB
/
dhcp_user_xdp.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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* SPDX-License-Identifier: GPL-2.0 */
static const char *__doc__ = "DHCP relay program to add Option 82\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 <arpa/inet.h>
#define SERVER_MAP "dhcp_server"
#define XDP_OBJ "dhcp_kern_xdp.o"
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "interface", required_argument, NULL,
'i' }, // Name of interface to run on
{ "dhcp-server", required_argument, NULL, 'd' },
{ "mode", required_argument, NULL, 'm' },
{ "unload", no_argument, NULL, 'u' },
{ 0, 0, NULL, 0 }
};
static void print_usage(char *argv[])
{
int i;
printf("Usage:\n");
printf("%s\n", argv[0]);
for (i = 0; options[i].name != 0; i++) {
printf(" --%-12s", options[i].name);
if (options[i].flag != NULL)
printf(" flag (internal value:%d)", *options[i].flag);
else
printf(" short-option: -%c", options[i].val);
printf("\n");
}
printf("Example:\n");
printf("To load program:\n %s -i eth0 -d 10.0.0.1\n", argv[0]);
printf("To unload program:\n %s -i eth0 -u\n", argv[0]);
printf("\n");
}
static int xdp_link_detach(int ifindex, __u32 xdp_flags)
{
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 -1;
}
return 0;
}
int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
{
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 < 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\n");
break;
case EOPNOTSUPP:
fprintf(stderr, "Hint: Native-XDP not supported\n");
break;
default:
break;
}
return -1;
}
return 0;
}
/* User program takes two or three arguments
* interface name, relay server IP and prog
* unload flag
*/
int main(int argc, char **argv)
{
char filename[256] = "dhcp_kern_xdp.o";
int prog_fd, err;
int opt;
__u32 xdp_flags = XDP_FLAGS_DRV_MODE;
char dev[IF_NAMESIZE] = "";
bool do_unload = 0;
struct bpf_map *map = NULL;
struct bpf_obj *obj = NULL;
int map_fd;
int key = 0;
char server[15] = "";
struct in_addr addr;
__u16 ifindex;
while ((opt = getopt_long(argc, argv, "hui:d:m:", options, NULL)) !=
-1) {
switch (opt) {
case 'i':
strncpy(dev, optarg, IF_NAMESIZE);
dev[IF_NAMESIZE - 1] = '\0';
ifindex = if_nametoindex(dev);
if (ifindex <= 0) {
printf("Couldn't find ifname:%s \n", dev);
return -EINVAL;
}
break;
case 'd':
if (inet_aton(optarg, &addr) == 0) {
fprintf(stderr,
"Couldn't validate IP address:%s\n",
optarg);
return -EINVAL;
}
break;
case 'm':
if (strcmp(optarg, "skb") == 0) {
xdp_flags = XDP_FLAGS_SKB_MODE;
} else if (strcmp(optarg, "drv") != 0) {
fprintf(stderr, "Invalid mode: %s\n", optarg);
return -EINVAL;
}
break;
case 'u':
do_unload = 1;
break;
case 'h':
print_usage(argv);
exit(0);
default:
fprintf(stderr, "Unknown option %s\n", argv[optind]);
return -EINVAL;
}
}
if (do_unload)
return xdp_link_detach(ifindex, xdp_flags);
/* Load the BPF-ELF object file and get back first BPF_prog FD */
err = bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
if (err) {
fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n",
filename, err, strerror(-err));
return -1;
}
if (prog_fd <= 0) {
printf("ERR: loading file: %s\n");
return -1;
}
/* read the map from prog object file and update the real
* server IP to the map
*/
map = bpf_object__find_map_by_name(obj, SERVER_MAP);
err = libbpf_get_error(map);
if (err) {
fprintf(stderr, "Could not find map %s in %s: %s\n", SERVER_MAP,
XDP_OBJ, strerror(err));
map = NULL;
exit(-1);
}
map_fd = bpf_map__fd(map);
if (map_fd < 0) {
fprintf(stderr, "Could not get map fd\n");
exit(-1);
}
err = bpf_map_update_elem(map_fd, &key, &addr.s_addr, BPF_ANY);
if (err) {
fprintf(stderr, "Could not update map %s in %s\n", SERVER_MAP,
XDP_OBJ);
exit(-1);
}
err = xdp_link_attach(ifindex, xdp_flags, prog_fd);
if (err)
return err;
printf("Success: Loading xdp program\n");
return 0;
}