diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..8075592 --- /dev/null +++ b/src/main.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2024-2025 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +/* Funded through the NGI0 Entrust established by NLnet (https://nlnet.nl) + * with support from the European Commission's Next Generation Internet + * programme. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <getopt.h> + +#include "test.h" + +static int run_test_dir(const char *filename, struct nft_test_ctx *ctx, + struct nft_test_stats *stats) +{ + struct dirent *dent; + char path[PATH_MAX]; + struct stat st; + int ret = 0; + DIR *d; + + d = opendir(filename); + if (!d) { + perror("cannot open directory"); + return -1; + } + + dent = readdir(d); + while (dent) { + if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) { + dent = readdir(d); + continue; + } + snprintf(path, sizeof(path), "%s/%s", filename, dent->d_name); + if (stat(path, &st) < 0) { + fprintf(stderr, "cannot open file %s: %s\n", + path, strerror(errno)); + return -1; + } + if (S_ISDIR(st.st_mode)) { + if (run_test_dir(path, ctx, stats) < 0) { + ret = -1; + break; + } + dent = readdir(d); + continue; + } else if (!S_ISREG(st.st_mode)) { + dent = readdir(d); + continue; + } + if (run_test(path, ctx) < 0) + stats->failed++; + else + stats->ok++; + + if (ctx->fatal) { + ret = -1; + break; + } + + dent = readdir(d); + } + closedir(d); + + return ret; +} + +static int parse_fuzz_mode(char *str, uint32_t *type, uint32_t *num_types) +{ + char *x, *token; + int a, idx = 0; + + x = strchr(str, ','); + + do { + if (idx >= FUZZ_STACK_MAX) { + printf("too many modes\n"); + return -1; + } + + if (!x) { + token = str; + a = fuzz_type_to_value(token); + if (a < 0) { + printf("unknown fuzzing mode %s\n", token); + printf("Expected:\n", token); + print_fuzz_modes(); + return -1; + } + type[idx++] = a; + break; + } + + *x = '\0'; + token = str; + a = fuzz_type_to_value(token); + if (a < 0) { + printf("unknown fuzzing mode %s\n", token); + printf("Expected:\n", token); + print_fuzz_modes(); + return -1; + } + type[idx++] = a; + + str = x + 1; + + x = strchr(str, ','); + } while (1); + + *num_types = idx; + + return idx; +} + +bool errout; +bool noflush; + +int main(int argc, char *argv[]) +{ + struct timeval tv_start, tv_end, tv_diff; + struct nft_test_stats stats = {}; + struct nft_test_ctx ctx = {}; + const char *filename; + struct stat st; + int opt; + + while ((opt = getopt(argc, argv, "nf:hedc")) != -1) { + switch (opt) { + case 'n': + noflush = true; + break; + case 'f': + ctx.fuzz = true; + if (parse_fuzz_mode(optarg, ctx.fuzz_ctx.type, &ctx.fuzz_ctx.num_types) < 0) + exit(EXIT_FAILURE); + break; + case 'd': + ctx.fuzz_ctx.debug = true; + break; + case 'e': + errout = true; + break; + case 'c': + ctx.dryrun = true; + break; + case 'h': + default: + fprintf(stderr, "Usage: %s [-e] [-d] [-n] [-c] [-f <mode,...>] <input>\n", + argv[0]); + printf("Options:\n"); + printf("\t-e\tdisplay error reported by kernel\n"); + printf("\t-n\tdo not flush previous ruleset\n"); + printf("\t-d\tdisplay debugging information\n"); + printf("\t-c\tdry run\n"); + printf("\t-f <mode>\tfuzz test, see fuzz modes\n"); + printf("Available fuzz modes:\n"); + print_fuzz_modes(); + exit(EXIT_FAILURE); + break; + } + } + + if (optind >= argc) { + fprintf(stderr, "ERROR: specify path to file or folder with tests\n"); + exit(EXIT_FAILURE); + } + + filename = argv[optind]; + + /* TODO: add dummy device with netlink. */ + system("sudo modprobe xt_cpu"); + system("sudo ip link add dummy0 type dummy > /dev/null 2>&1"); + system("sudo ip link add dummy1 type dummy > /dev/null 2>&1"); + system("sudo ip link add dummy2 type dummy > /dev/null 2>&1"); + system("sudo ip link add dummy3 type dummy > /dev/null 2>&1"); + + gettimeofday(&tv_start, NULL); + + if (!strcmp(filename, "-")) + filename = "/dev/stdin"; + + if (stat(filename, &st) < 0) { + fprintf(stderr, "cannot open test file %s: %s\n", + filename, strerror(errno)); + return EXIT_FAILURE; + } + if (S_ISDIR(st.st_mode)) { + run_test_dir(filename, &ctx, &stats); + printf("results: [\x1b[32mOK\x1b[37m] %d [\x1b[31mFAILED\x1b[37m] %d\n", + stats.ok, stats.failed); + } else if (S_ISREG(st.st_mode)) { + printf("%s...\n", filename); + run_test(filename, &ctx); + } + + gettimeofday(&tv_end, NULL); + timersub(&tv_end, &tv_start, &tv_diff); + + if (ctx.fuzz) + printf("total fuzz steps: %u in %u.%06u seconds\n", ctx.fuzz_ctx.total_fuzz_steps, tv_diff.tv_sec, tv_diff.tv_usec); + else + printf("done in %u.%06u seconds\n", tv_diff.tv_sec, tv_diff.tv_usec); + + return EXIT_SUCCESS; +} |