Implement support for LibXDP and improve code.
This commit is contained in:
185
src/xdpfw.c
185
src/xdpfw.c
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <bpf.h>
|
#include <bpf.h>
|
||||||
#include <libbpf.h>
|
#include <libbpf.h>
|
||||||
|
#include <xdp/libxdp.h>
|
||||||
|
|
||||||
#include "xdpfw.h"
|
#include "xdpfw.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@@ -106,17 +107,25 @@ int updateconfig(struct config *cfg, char *cfgfile)
|
|||||||
/**
|
/**
|
||||||
* Finds a BPF map's FD.
|
* Finds a BPF map's FD.
|
||||||
*
|
*
|
||||||
* @param bpf_obj A pointer to the BPF object.
|
* @param prog A pointer to the XDP program structure.
|
||||||
* @param mapname The name of the map to retrieve.
|
* @param mapname The name of the map to retrieve.
|
||||||
*
|
*
|
||||||
* @return The map's FD.
|
* @return The map's FD.
|
||||||
*/
|
*/
|
||||||
int findmapfd(struct bpf_object *bpf_obj, const char *mapname)
|
int findmapfd(struct xdp_program *prog, const char *mapname)
|
||||||
{
|
{
|
||||||
struct bpf_map *map;
|
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
|
|
||||||
map = bpf_object__find_map_by_name(bpf_obj, mapname);
|
struct bpf_object *obj = xdp_program__bpf_obj(prog);
|
||||||
|
|
||||||
|
if (obj == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error finding BPF object from XDP program.\n");
|
||||||
|
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bpf_map *map = bpf_object__find_map_by_name(obj, mapname);
|
||||||
|
|
||||||
if (!map)
|
if (!map)
|
||||||
{
|
{
|
||||||
@@ -136,160 +145,117 @@ int findmapfd(struct bpf_object *bpf_obj, const char *mapname)
|
|||||||
*
|
*
|
||||||
* @param filename The path to the BPF object file.
|
* @param filename The path to the BPF object file.
|
||||||
*
|
*
|
||||||
* @return BPF's program FD.
|
* @return XDP program structure (pointer) or NULL.
|
||||||
*/
|
*/
|
||||||
int loadbpfobj(const char *filename, __u8 offload, int ifidx)
|
struct xdp_program *loadbpfobj(const char *filename)
|
||||||
{
|
{
|
||||||
int fd = -1;
|
struct xdp_program *prog = xdp_program__open_file(filename, "xdp_prog", NULL);
|
||||||
|
|
||||||
// Create attributes and assign XDP type + file name.
|
if (prog == NULL)
|
||||||
struct bpf_prog_load_attr attrs =
|
|
||||||
{
|
{
|
||||||
.prog_type = BPF_PROG_TYPE_XDP,
|
// The main function handles this error.
|
||||||
};
|
return NULL;
|
||||||
|
|
||||||
// If we want to offload the XDP program, we must send the ifindex item to the interface's index.
|
|
||||||
if (offload)
|
|
||||||
{
|
|
||||||
attrs.ifindex = ifidx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs.file = filename;
|
return prog;
|
||||||
|
|
||||||
// Check if we can access the BPF object file.
|
|
||||||
if (access(filename, O_RDONLY) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Could not read/access BPF object file :: %s (%s).\n", filename, strerror(errno));
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct bpf_object *obj = NULL;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
// Load the BPF object file itself.
|
|
||||||
err = bpf_prog_load_xattr(&attrs, &obj, &fd);
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Could not load XDP BPF program :: %s.\n", strerror(errno));
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct bpf_program *prog;
|
|
||||||
|
|
||||||
// Load the BPF program itself by section name and try to retrieve FD.
|
|
||||||
prog = bpf_object__find_program_by_title(obj, "xdp_prog");
|
|
||||||
fd = bpf_program__fd(prog);
|
|
||||||
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
printf("XDP program not found by section/title :: xdp_prog (%s).\n", strerror(fd));
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve BPF maps.
|
|
||||||
filtersmap = findmapfd(obj, "filters_map");
|
|
||||||
statsmap = findmapfd(obj, "stats_map");
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to attach or detach (progfd = -1) a BPF/XDP program to an interface.
|
* Attempts to attach or detach (progfd = -1) a BPF/XDP program to an interface.
|
||||||
*
|
*
|
||||||
|
* @param prog A pointer to the XDP program structure.
|
||||||
* @param ifidx The index to the interface to attach to.
|
* @param ifidx The index to the interface to attach to.
|
||||||
* @param progfd A file description (FD) to the BPF/XDP program.
|
* @param detach If above 0, attempts to detach XDP program.
|
||||||
* @param cmd A pointer to a cmdline struct that includes command line arguments (mostly checking for offload/HW mode set).
|
* @param cmd A pointer to a cmdline struct that includes command line arguments (mostly checking for offload/HW mode set).
|
||||||
*
|
*
|
||||||
* @return Returns the flag (int) it successfully attached the BPF/XDP program with or a negative value for error.
|
* @return 0 on success and 1 on error.
|
||||||
*/
|
*/
|
||||||
int attachxdp(int ifidx, int progfd, struct cmdline *cmd)
|
int attachxdp(struct xdp_program *prog, int ifidx, __u8 detach, struct cmdline *cmd)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
__u32 mode = XDP_MODE_NATIVE;
|
||||||
char *smode;
|
char *smode;
|
||||||
|
|
||||||
__u32 flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
|
||||||
__u32 mode = XDP_FLAGS_DRV_MODE;
|
|
||||||
|
|
||||||
smode = "DRV/native";
|
smode = "DRV/native";
|
||||||
|
|
||||||
if (cmd->offload)
|
if (cmd->offload)
|
||||||
{
|
{
|
||||||
smode = "HW/offload";
|
smode = "HW/offload";
|
||||||
|
|
||||||
mode = XDP_FLAGS_HW_MODE;
|
mode = XDP_MODE_HW;
|
||||||
}
|
}
|
||||||
else if (cmd->skb)
|
else if (cmd->skb)
|
||||||
{
|
{
|
||||||
smode = "SKB/generic";
|
smode = "SKB/generic";
|
||||||
mode = XDP_FLAGS_SKB_MODE;
|
mode = XDP_MODE_SKB;
|
||||||
}
|
}
|
||||||
|
|
||||||
flags |= mode;
|
__u8 exit = 0;
|
||||||
|
|
||||||
int exit = 0;
|
|
||||||
|
|
||||||
while (!exit)
|
while (!exit)
|
||||||
{
|
{
|
||||||
// Try loading program with current mode.
|
// Try loading program with current mode.
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = bpf_set_link_xdp_fd(ifidx, progfd, flags);
|
if (detach)
|
||||||
|
|
||||||
if (err || progfd == -1)
|
|
||||||
{
|
{
|
||||||
const char *errmode;
|
err = xdp_program__detach(prog, ifidx, mode, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = xdp_program__attach(prog, ifidx, mode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Could not attach with mode %s (%s) (%d).\n", smode, strerror(-err), -err);
|
||||||
|
}
|
||||||
|
|
||||||
// Decrease mode.
|
// Decrease mode.
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case XDP_FLAGS_HW_MODE:
|
case XDP_MODE_HW:
|
||||||
mode = XDP_FLAGS_DRV_MODE;
|
mode = XDP_MODE_NATIVE;
|
||||||
flags &= ~XDP_FLAGS_HW_MODE;
|
smode = "DRV/native";
|
||||||
errmode = "HW/offload";
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XDP_FLAGS_DRV_MODE:
|
case XDP_MODE_NATIVE:
|
||||||
mode = XDP_FLAGS_SKB_MODE;
|
mode = XDP_MODE_SKB;
|
||||||
flags &= ~XDP_FLAGS_DRV_MODE;
|
smode = "SKB/generic";
|
||||||
errmode = "DRV/native";
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XDP_FLAGS_SKB_MODE:
|
case XDP_MODE_SKB:
|
||||||
// Exit program and set mode to -1 indicating error.
|
// Exit loop.
|
||||||
exit = 1;
|
exit = 1;
|
||||||
mode = -err;
|
smode = NULL;
|
||||||
errmode = "SKB/generic";
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progfd != -1)
|
// Retry.
|
||||||
{
|
continue;
|
||||||
fprintf(stderr, "Could not attach with %s mode (%s)(%d).\n", errmode, strerror(-err), err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode != -err)
|
|
||||||
{
|
|
||||||
smode = (mode == XDP_FLAGS_HW_MODE) ? "HW/offload" : (mode == XDP_FLAGS_DRV_MODE) ? "DRV/native" : (mode == XDP_FLAGS_SKB_MODE) ? "SKB/generic" : "N/A";
|
|
||||||
flags |= mode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stdout, "Loaded XDP program in %s mode.\n", smode);
|
|
||||||
|
|
||||||
break;
|
// Success, so break current loop.
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mode;
|
// If exit is set to 1 or smode is NULL, it indicates full failure.
|
||||||
|
if (exit || smode == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error attaching XDP program.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stdout, "Loaded XDP program on mode %s.\n", smode);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat conf_stat;
|
struct stat conf_stat;
|
||||||
@@ -452,13 +418,12 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XDP variables.
|
// XDP variables.
|
||||||
int progfd;
|
|
||||||
const char *filename = "/etc/xdpfw/xdpfw_kern.o";
|
const char *filename = "/etc/xdpfw/xdpfw_kern.o";
|
||||||
|
|
||||||
// Get XDP's ID.
|
// Load BPF object.
|
||||||
progfd = loadbpfobj(filename, cmd.offload, ifidx);
|
struct xdp_program *prog = loadbpfobj(filename);
|
||||||
|
|
||||||
if (progfd <= 0)
|
if (prog == NULL)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error loading eBPF object file. File name => %s.\n", filename);
|
fprintf(stderr, "Error loading eBPF object file. File name => %s.\n", filename);
|
||||||
|
|
||||||
@@ -466,15 +431,15 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attach XDP program.
|
// Attach XDP program.
|
||||||
int res = attachxdp(ifidx, progfd, &cmd);
|
if (attachxdp(prog, ifidx, 0, &cmd))
|
||||||
|
|
||||||
if (res != XDP_FLAGS_HW_MODE && res != XDP_FLAGS_DRV_MODE && res != XDP_FLAGS_SKB_MODE)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error attaching XDP program :: %s (%d)\n", strerror(res), res);
|
|
||||||
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve BPF maps.
|
||||||
|
filtersmap = findmapfd(prog, "filters_map");
|
||||||
|
statsmap = findmapfd(prog, "stats_map");
|
||||||
|
|
||||||
// Check for valid maps.
|
// Check for valid maps.
|
||||||
if (filtersmap < 0)
|
if (filtersmap < 0)
|
||||||
{
|
{
|
||||||
@@ -578,7 +543,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detach XDP program.
|
// Detach XDP program.
|
||||||
attachxdp(ifidx, -1, &cmd);
|
attachxdp(prog, ifidx, 1, &cmd);
|
||||||
|
|
||||||
// Add spacing.
|
// Add spacing.
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include <linux/bpf_common.h>
|
#include <linux/bpf_common.h>
|
||||||
|
|
||||||
#include <bpf_helpers.h>
|
#include <bpf_helpers.h>
|
||||||
|
#include <xdp/xdp_helpers.h>
|
||||||
|
#include <xdp/prog_dispatcher.h>
|
||||||
|
|
||||||
#include "xdpfw.h"
|
#include "xdpfw.h"
|
||||||
|
|
||||||
@@ -28,53 +30,59 @@
|
|||||||
#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
|
#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") filters_map =
|
struct
|
||||||
{
|
{
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(priority, 10);
|
||||||
.key_size = sizeof(__u32),
|
__uint(XDP_PASS, 1);
|
||||||
.value_size = sizeof(struct filter),
|
} XDP_RUN_CONFIG(xdp_prog_main);
|
||||||
.max_entries = MAX_FILTERS
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") stats_map =
|
struct
|
||||||
{
|
{
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(__u32),
|
__uint(max_entries, MAX_FILTERS);
|
||||||
.value_size = sizeof(struct stats),
|
__type(key, __u32);
|
||||||
.max_entries = 1
|
__type(value, struct filter);
|
||||||
};
|
} filters_map SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") ip_stats_map =
|
struct
|
||||||
{
|
{
|
||||||
.type = BPF_MAP_TYPE_LRU_HASH,
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
.key_size = sizeof(__u32),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(struct ip_stats),
|
__type(key, __u32);
|
||||||
.max_entries = MAX_TRACK_IPS
|
__type(value, struct stats);
|
||||||
};
|
} stats_map SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") ip_blacklist_map =
|
struct
|
||||||
{
|
{
|
||||||
.type = BPF_MAP_TYPE_LRU_HASH,
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
.key_size = sizeof(__u32),
|
__uint(max_entries, MAX_TRACK_IPS);
|
||||||
.value_size = sizeof(__u64),
|
__type(key, __u32);
|
||||||
.max_entries = MAX_TRACK_IPS
|
__type(value, struct ip_stats);
|
||||||
};
|
} ip_stats_map SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") ip6_stats_map =
|
struct
|
||||||
{
|
{
|
||||||
.type = BPF_MAP_TYPE_LRU_HASH,
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
.key_size = sizeof(__u128),
|
__uint(max_entries, MAX_TRACK_IPS);
|
||||||
.value_size = sizeof(struct ip_stats),
|
__type(key, __u32);
|
||||||
.max_entries = MAX_TRACK_IPS
|
__type(value, __u64);
|
||||||
};
|
} ip_blacklist_map SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") ip6_blacklist_map =
|
struct
|
||||||
{
|
{
|
||||||
.type = BPF_MAP_TYPE_LRU_HASH,
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
.key_size = sizeof(__u128),
|
__uint(max_entries, MAX_TRACK_IPS);
|
||||||
.value_size = sizeof(__u64),
|
__type(key, __u128);
|
||||||
.max_entries = MAX_TRACK_IPS
|
__type(value, struct ip_stats);
|
||||||
};
|
} ip6_stats_map SEC(".maps");
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, MAX_TRACK_IPS);
|
||||||
|
__type(key, __u128);
|
||||||
|
__type(value, __u64);
|
||||||
|
} ip6_blacklist_map SEC(".maps");
|
||||||
|
|
||||||
SEC("xdp_prog")
|
SEC("xdp_prog")
|
||||||
int xdp_prog_main(struct xdp_md *ctx)
|
int xdp_prog_main(struct xdp_md *ctx)
|
||||||
@@ -634,3 +642,5 @@ int xdp_prog_main(struct xdp_md *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
__uint(xsk_prog_version, XDP_DISPATCHER_VERSION) SEC(XDP_METADATA_SECTION);
|
||||||
Reference in New Issue
Block a user