mirror of
https://github.com/xdp-project/bpf-examples.git
synced 2024-05-06 15:54:53 +00:00
Add DHCP relay program.
Initial working version of DHCP relay using XDP is created. Currently, this code has user program and a xdp ebpf program. User program takes network interface and dhcp relay server IP as inputs and store it in a map. XDP program filters the incoming DHCP requests and inserts option 82 in the DHCP request packets and overwrites the destination IP to that of DHCP relay server IP.An optional argu -ment for user program is also provided to unload the xdp program. README file provides to instructions to build and load the xdp program. Signed-off-by: Sachin Tiptur <sachin.tiptur.satyanarayana.gupta@hof-university.de> [ whitespace fixes ] Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
This commit is contained in:
committed by
Toke Høiland-Jørgensen
parent
7bb3c6ac91
commit
cb9fc3025a
198
dhcp-relay/dhcp_user_xdp.c
Normal file
198
dhcp-relay/dhcp_user_xdp.c
Normal file
@@ -0,0 +1,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;
|
||||
}
|
Reference in New Issue
Block a user