diff --git a/.gitignore b/.gitignore index 36e04aa..1613ae7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode/ xdpfw xdpfw.s -xdpfw.conf \ No newline at end of file +xdpfw.conf +*.log \ No newline at end of file diff --git a/Makefile b/Makefile index e2701c2..6f6ac8a 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ LOADER_DIR = $(SRC_DIR)/loader XDP_DIR = $(SRC_DIR)/xdp ETC_DIR = /etc/xdpfw +LOG_DIR = /var/log/xdpfw # Additional build directories. BUILD_LOADER_DIR = $(BUILD_DIR)/loader @@ -52,6 +53,9 @@ LOADER_UTILS_CMDLINE_OBJ = cmdline.o LOADER_UTILS_XDP_SRC = xdp.c LOADER_UTILS_XDP_OBJ = xdp.o +LOADER_UTILS_LOGGING_SRC = logging.c +LOADER_UTILS_LOGGING_OBJ = logging.o + LOADER_UTILS_STATS_SRC = stats.c LOADER_UTILS_STATS_OBJ = stats.o @@ -59,7 +63,7 @@ LOADER_UTILS_HELPERS_SRC = helpers.c LOADER_UTILS_HELPERS_OBJ = helpers.o # Loader objects. -LOADER_OBJS = $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CMDLINE_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_XDP_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_STATS_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ) +LOADER_OBJS = $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CMDLINE_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_XDP_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_LOGGING_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_STATS_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ) ifeq ($(LIBXDP_STATIC), 1) LOADER_OBJS := $(LIBBPF_OBJS) $(LIBXDP_OBJS) $(LOADER_OBJS) @@ -93,7 +97,7 @@ all: loader xdp loader: loader_utils $(CC) $(INCS) $(FLAGS) $(FLAGS_LOADER) -o $(BUILD_LOADER_DIR)/$(LOADER_OUT) $(LOADER_OBJS) $(LOADER_DIR)/$(LOADER_SRC) -loader_utils: loader_utils_config loader_utils_cmdline loader_utils_helpers loader_utils_xdp loader_utils_stats +loader_utils: loader_utils_config loader_utils_cmdline loader_utils_helpers loader_utils_xdp loader_utils_logging loader_utils_stats loader_utils_config: $(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_CONFIG_SRC) @@ -104,6 +108,9 @@ loader_utils_cmdline: loader_utils_xdp: $(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_XDP_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_XDP_SRC) +loader_utils_logging: + $(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_LOGGING_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_LOGGING_SRC) + loader_utils_stats: $(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_STATS_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_STATS_SRC) @@ -128,6 +135,8 @@ libxdp_clean: install: mkdir -p $(ETC_DIR) + mkdir -p $(LOG_DIR) + cp -n xdpfw.conf.example $(ETC_DIR)/xdpfw.conf cp -n other/xdpfw.service /etc/systemd/system/ diff --git a/src/loader/prog.c b/src/loader/prog.c index 8009134..f1eac1e 100644 --- a/src/loader/prog.c +++ b/src/loader/prog.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -37,16 +38,6 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } - // Raise RLimit. - struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; - - if (setrlimit(RLIMIT_MEMLOCK, &rl)) - { - fprintf(stderr, "[ERROR] Failed to raise rlimit. Please make sure this program is ran as root!\n"); - - return EXIT_FAILURE; - } - // Initialize config. config__t cfg = {0}; @@ -68,54 +59,87 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } + LogMsg(&cfg, 2, 0, "Raising RLimit..."); + + // Raise RLimit. + struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; + + if (setrlimit(RLIMIT_MEMLOCK, &rl)) + { + LogMsg(&cfg, 0, 1, "[ERROR] Failed to raise rlimit. Please make sure this program is ran as root!\n"); + + return EXIT_FAILURE; + } + + LogMsg(&cfg, 2, 0, "Retrieving interface index for '%s'...", cfg.interface); + // Get interface index. int ifidx = if_nametoindex(cfg.interface); if (ifidx < 0) { - fprintf(stderr, "[ERROR] Failed to retrieve index of network interface '%s'.\n", cfg.interface); + LogMsg(&cfg, 0, 1, "[ERROR] Failed to retrieve index of network interface '%s'.\n", cfg.interface); return EXIT_FAILURE; } + LogMsg(&cfg, 2, 0, "Loading XDP/BPF program at '%s'...", XDP_OBJ_PATH); + // Load BPF object. struct xdp_program *prog = LoadBpfObj(XDP_OBJ_PATH); if (prog == NULL) { - fprintf(stderr, "[ERROR] Failed to load eBPF object file. Object path => %s.\n", XDP_OBJ_PATH); + LogMsg(&cfg, 0, 1, "[ERROR] Failed to load eBPF object file. Object path => %s.\n", XDP_OBJ_PATH); return EXIT_FAILURE; } + + LogMsg(&cfg, 2, 0, "Attaching XDP program to interface '%s'...", cfg.interface); // Attach XDP program. - if ((ret = AttachXdp(prog, ifidx, 0, &cmd)) != 0) + char *mode_used = NULL; + + if ((ret = AttachXdp(prog, &mode_used, ifidx, 0, &cmd)) != 0) { - fprintf(stderr, "[ERROR] Failed to attach XDP program to interface '%s' (%d).\n", cfg.interface, ret); + LogMsg(&cfg, 0, 1, "[ERROR] Failed to attach XDP program to interface '%s' using available modes (%d).\n", cfg.interface, ret); return EXIT_FAILURE; } + if (mode_used != NULL) + { + LogMsg(&cfg, 1, 0, "Attached XDP program using mode '%s'...", mode_used); + } + + LogMsg(&cfg, 2, 0, "Retrieving BPF map FDs..."); + // Retrieve BPF maps. int filters_map = FindMapFd(prog, "filters_map"); // Check for valid maps. if (filters_map < 0) { - fprintf(stderr, "[ERROR] Failed to find 'filters_map' BPF map.\n"); + LogMsg(&cfg, 0, 1, "[ERROR] Failed to find 'filters_map' BPF map.\n"); return EXIT_FAILURE; } + LogMsg(&cfg, 3, 0, "filters_map FD => %d.", filters_map); + int stats_map = FindMapFd(prog, "stats_map"); if (stats_map < 0) { - fprintf(stderr, "[ERROR] Failed to find 'stats_map' BPF map.\n"); + LogMsg(&cfg, 0, 1, "[ERROR] Failed to find 'stats_map' BPF map.\n"); return EXIT_FAILURE; } + LogMsg(&cfg, 3, 0, "stats_map FD => %d.", stats_map); + + LogMsg(&cfg, 2, 0, "Updating filters..."); + // Update BPF maps. UpdateFilters(filters_map, &cfg); @@ -126,6 +150,8 @@ int main(int argc, char *argv[]) // Receive CPU count for stats map parsing. int cpus = get_nprocs_conf(); + LogMsg(&cfg, 4, 0, "Retrieved %d CPUs on host.", cpus); + unsigned int end_time = (cmd.time > 0) ? time(NULL) + cmd.time : 0; // Create last updated variables. @@ -159,7 +185,7 @@ int main(int argc, char *argv[]) // Update config. if ((ret = LoadConfig(&cfg, cmd.cfgfile)) != 0) { - fprintf(stderr, "[WARNING] Failed to load config after update check (%d)...\n", ret); + LogMsg(&cfg, 1, 0, "[WARNING] Failed to load config after update check (%d)...\n", ret); } // Update BPF maps. @@ -178,7 +204,7 @@ int main(int argc, char *argv[]) { if (CalculateStats(stats_map, cpus)) { - fprintf(stderr, "[WARNING] Failed to calculate packet stats. Stats map FD => %d...\n", stats_map); + LogMsg(&cfg, 1, 0, "[WARNING] Failed to calculate packet stats. Stats map FD => %d...\n", stats_map); } } @@ -188,14 +214,14 @@ int main(int argc, char *argv[]) fprintf(stdout, "\n"); // Detach XDP program. - if (AttachXdp(prog, ifidx, 1, &cmd)) + if (AttachXdp(prog, &mode_used, ifidx, 1, &cmd)) { - fprintf(stderr, "[ERROR] Failed to detach XDP program from interface '%s'.\n", cfg.interface); + LogMsg(&cfg, 0, 1, "[ERROR] Failed to detach XDP program from interface '%s'.\n", cfg.interface); return EXIT_FAILURE; } - fprintf(stdout, "Cleaned up and exiting...\n"); + LogMsg(&cfg, 1, 0, "Cleaned up and exiting...\n"); // Exit program successfully. return EXIT_SUCCESS; diff --git a/src/loader/utils/config.c b/src/loader/utils/config.c index b454d03..cab0bb3 100644 --- a/src/loader/utils/config.c +++ b/src/loader/utils/config.c @@ -46,6 +46,8 @@ int LoadConfig(config__t *cfg, char *cfg_file) */ void SetCfgDefaults(config__t *cfg) { + cfg->verbose = 1; + cfg->log_file = strdup("/var/log/xdpfw/xdpfw.log"); cfg->updatetime = 0; cfg->interface = NULL; cfg->nostats = 0; @@ -168,6 +170,35 @@ int ReadCfg(config__t *cfg) return EXIT_FAILURE; } + int verbose; + + if (config_lookup_int(&conf, "verbose", &verbose) == CONFIG_TRUE) + { + cfg->verbose = verbose; + } + + const char* log_file; + + if (config_lookup_string(&conf, "log_file", &log_file) == CONFIG_TRUE) + { + // We must free previous value to prevent memory leak. + if (cfg->log_file != NULL) + { + free(cfg->log_file); + cfg->log_file = NULL; + } + + if (strlen(log_file) > 0) + { + cfg->log_file = strdup(log_file); + + } + else + { + cfg->log_file = NULL; + } + } + // Get interface. const char *interface; diff --git a/src/loader/utils/config.h b/src/loader/utils/config.h index 72c169d..174b9b0 100644 --- a/src/loader/utils/config.h +++ b/src/loader/utils/config.h @@ -13,6 +13,8 @@ struct config { + int verbose; + char *log_file; char *interface; u16 updatetime; unsigned int nostats : 1; diff --git a/src/loader/utils/logging.c b/src/loader/utils/logging.c new file mode 100644 index 0000000..d45cafe --- /dev/null +++ b/src/loader/utils/logging.c @@ -0,0 +1,96 @@ +#include + +/** + * Prints a log message to stdout/stderr along with a file if specified. + * + * @param req_lvl The required level for this message. + * @param cur_lvl The current verbose level. + * @param error If 1, sets pipe to stderr instead of stdout. + * @param msg The log message. + * @param args A VA list of arguments for the message. + * + * @return void + */ +static void LogMsgRaw(int req_lvl, int cur_lvl, int error, const char* log_path, const char* msg, va_list args) +{ + if (cur_lvl < req_lvl) + { + return; + } + + FILE* pipe = stdout; + + if (error) + { + pipe = stderr; + } + + // We need to format the message. + va_list args_copy; + va_copy(args_copy, args); + int len = vsnprintf(NULL, 0, msg, args_copy); + va_end(args_copy); + + if (len < 0) + { + return; + } + + char f_msg[len + 1]; + vsnprintf(f_msg, sizeof(f_msg), msg, args); + + char full_msg[len + 6 + 1]; + snprintf(full_msg, sizeof(full_msg), "[%d] %s", req_lvl, f_msg); + + fprintf(pipe, "%s\n", full_msg); + + if (log_path != NULL) + { + FILE* log_file = fopen(log_path, "a"); + + if (!log_file) + { + return; + } + + time_t now = time(NULL); + struct tm* tm_val = localtime(&now); + + if (!tm_val) + { + fclose(log_file); + + return; + } + + + char log_file_msg[len + 22 + 1]; + + snprintf(log_file_msg, sizeof(log_file_msg), "[%02d-%02d-%02d %02d:%02d:%02d]%s", tm_val->tm_year % 100, tm_val->tm_mon + 1, tm_val->tm_mday, + tm_val->tm_hour, tm_val->tm_min, tm_val->tm_sec, full_msg); + + fprintf(log_file, "%s\n", log_file_msg); + + fclose(log_file); + } +} + +/** + * Prints a log message using LogMsgRaw(). + * + * @param cfg A pointer to the config structure. + * @param req_lvl The required level for this message. + * @param error Whether this is an error. + * @param msg The log message with format support. + * + * @return void + */ +void LogMsg(config__t* cfg, int req_lvl, int error, const char* msg, ...) +{ + va_list args; + va_start(args, msg); + + LogMsgRaw(req_lvl, cfg->verbose, error, (const char*)cfg->log_file, msg, args); + + va_end(args); +} \ No newline at end of file diff --git a/src/loader/utils/logging.h b/src/loader/utils/logging.h new file mode 100644 index 0000000..9047191 --- /dev/null +++ b/src/loader/utils/logging.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +#include + +#include + +void LogMsg(config__t* cfg, int req_lvl, int error, const char* msg, ...); \ No newline at end of file diff --git a/src/loader/utils/xdp.c b/src/loader/utils/xdp.c index 4bc51cc..9e3184e 100644 --- a/src/loader/utils/xdp.c +++ b/src/loader/utils/xdp.c @@ -60,31 +60,32 @@ struct xdp_program *LoadBpfObj(const char *file_name) * Attempts to attach or detach (progfd = -1) a BPF/XDP program to an interface. * * @param prog A pointer to the XDP program structure. + * @param mode_used The mode being used. * @param ifidx The index to the interface to attach to. * @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). * * @return 0 on success and 1 on error. */ -int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd) +int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, u8 detach, cmdline_t *cmd) { int err; - u32 mode = XDP_MODE_NATIVE; - char *smode; + u32 attach_mode = XDP_MODE_NATIVE; - smode = "DRV/native"; + *mode = "DRV/native"; if (cmd->offload) { - smode = "HW/offload"; + *mode = "HW/offload"; - mode = XDP_MODE_HW; + attach_mode = XDP_MODE_HW; } else if (cmd->skb) { - smode = "SKB/generic"; - mode = XDP_MODE_SKB; + *mode = "SKB/generic"; + + attach_mode = XDP_MODE_SKB; } int exit = 0; @@ -96,39 +97,35 @@ int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd) if (detach) { - err = xdp_program__detach(prog, ifidx, mode, 0); + err = xdp_program__detach(prog, ifidx, attach_mode, 0); } else { - err = xdp_program__attach(prog, ifidx, mode, 0); + err = xdp_program__attach(prog, ifidx, attach_mode, 0); } if (err) { - if (err) - { - fprintf(stderr, "Could not attach with mode %s (%s) (%d).\n", smode, strerror(-err), -err); - } - // Decrease mode. - switch (mode) + switch (attach_mode) { case XDP_MODE_HW: - mode = XDP_MODE_NATIVE; - smode = "DRV/native"; + attach_mode = XDP_MODE_NATIVE; + *mode = "DRV/native"; break; case XDP_MODE_NATIVE: - mode = XDP_MODE_SKB; - smode = "SKB/generic"; + attach_mode = XDP_MODE_SKB; + *mode = "SKB/generic"; break; case XDP_MODE_SKB: // Exit loop. exit = 1; - smode = NULL; + + *mode = NULL; break; } @@ -142,16 +139,11 @@ int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd) } // If exit is set to 1 or smode is NULL, it indicates full failure. - if (exit || smode == NULL) + if (exit || *mode == NULL) { return EXIT_FAILURE; } - if (detach < 1) - { - fprintf(stdout, "Loaded XDP program on mode %s...\n", smode); - } - return EXIT_SUCCESS; } diff --git a/src/loader/utils/xdp.h b/src/loader/utils/xdp.h index 9b7b127..6b7ed6c 100644 --- a/src/loader/utils/xdp.h +++ b/src/loader/utils/xdp.h @@ -12,5 +12,5 @@ int FindMapFd(struct xdp_program *prog, const char *map_name); struct xdp_program *LoadBpfObj(const char *file_name); -int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd); +int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, u8 detach, cmdline_t *cmd); void UpdateFilters(int filters_map, config__t *cfg); \ No newline at end of file