Blame src/main.c

Packit c5a612
/*
Packit c5a612
 * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
Packit c5a612
 *
Packit c5a612
 * This program is free software; you can redistribute it and/or modify
Packit c5a612
 * it under the terms of the GNU General Public License version 2 as
Packit c5a612
 * published by the Free Software Foundation.
Packit c5a612
 *
Packit c5a612
 * Development of this code funded by Astaro AG (http://www.astaro.com/)
Packit c5a612
 */
Packit c5a612
Packit c5a612
#include <stdlib.h>
Packit c5a612
#include <stddef.h>
Packit c5a612
#include <unistd.h>
Packit c5a612
#include <stdio.h>
Packit c5a612
#include <errno.h>
Packit c5a612
#include <string.h>
Packit c5a612
#include <getopt.h>
Packit c5a612
#include <fcntl.h>
Packit c5a612
#include <sys/types.h>
Packit c5a612
Packit c5a612
#include <nftables/libnftables.h>
Packit c5a612
#include <utils.h>
Packit c5a612
#include <cli.h>
Packit c5a612
Packit c5a612
static struct nft_ctx *nft;
Packit c5a612
Packit c5a612
enum opt_vals {
Packit c5a612
	OPT_HELP		= 'h',
Packit c5a612
	OPT_VERSION		= 'v',
Packit c5a612
	OPT_CHECK		= 'c',
Packit c5a612
	OPT_FILE		= 'f',
Packit c5a612
	OPT_INTERACTIVE		= 'i',
Packit c5a612
	OPT_INCLUDEPATH		= 'I',
Packit c5a612
	OPT_JSON		= 'j',
Packit c5a612
	OPT_NUMERIC		= 'n',
Packit c5a612
	OPT_STATELESS		= 's',
Packit c5a612
	OPT_IP2NAME		= 'N',
Packit c5a612
	OPT_SERVICE		= 'S',
Packit c5a612
	OPT_DEBUG		= 'd',
Packit c5a612
	OPT_HANDLE_OUTPUT	= 'a',
Packit c5a612
	OPT_ECHO		= 'e',
Packit c5a612
	OPT_GUID		= 'u',
Packit c5a612
	OPT_NUMERIC_PRIO	= 'y',
Packit c5a612
	OPT_NUMERIC_PROTO	= 'p',
Packit c5a612
	OPT_NUMERIC_TIME	= 'T',
Packit c5a612
	OPT_TERSE		= 't',
Packit c5a612
	OPT_INVALID		= '?',
Packit c5a612
};
Packit Service 1894c6
#define OPTSTRING	"+hvd:cf:iI:jvnsNaeSupypTt"
Packit c5a612
Packit c5a612
static const struct option options[] = {
Packit c5a612
	{
Packit c5a612
		.name		= "help",
Packit c5a612
		.val		= OPT_HELP,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "version",
Packit c5a612
		.val		= OPT_VERSION,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "check",
Packit c5a612
		.val		= OPT_CHECK,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "file",
Packit c5a612
		.val		= OPT_FILE,
Packit c5a612
		.has_arg	= 1,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "interactive",
Packit c5a612
		.val		= OPT_INTERACTIVE,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "numeric",
Packit c5a612
		.val		= OPT_NUMERIC,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "stateless",
Packit c5a612
		.val		= OPT_STATELESS,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "reversedns",
Packit c5a612
		.val		= OPT_IP2NAME,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "service",
Packit c5a612
		.val		= OPT_SERVICE,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "includepath",
Packit c5a612
		.val		= OPT_INCLUDEPATH,
Packit c5a612
		.has_arg	= 1,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "debug",
Packit c5a612
		.val		= OPT_DEBUG,
Packit c5a612
		.has_arg	= 1,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "handle",
Packit c5a612
		.val		= OPT_HANDLE_OUTPUT,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "echo",
Packit c5a612
		.val		= OPT_ECHO,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "json",
Packit c5a612
		.val		= OPT_JSON,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "guid",
Packit c5a612
		.val		= OPT_GUID,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "numeric-priority",
Packit c5a612
		.val		= OPT_NUMERIC_PRIO,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "numeric-protocol",
Packit c5a612
		.val		= OPT_NUMERIC_PROTO,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "numeric-time",
Packit c5a612
		.val		= OPT_NUMERIC_TIME,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "terse",
Packit c5a612
		.val		= OPT_TERSE,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= NULL
Packit c5a612
	}
Packit c5a612
};
Packit c5a612
Packit c5a612
static void show_help(const char *name)
Packit c5a612
{
Packit c5a612
	printf(
Packit c5a612
"Usage: %s [ options ] [ cmds... ]\n"
Packit c5a612
"\n"
Packit c5a612
"Options:\n"
Packit c5a612
"  -h, --help			Show this help\n"
Packit c5a612
"  -v, --version			Show version information\n"
Packit c5a612
"\n"
Packit c5a612
"  -c, --check			Check commands validity without actually applying the changes.\n"
Packit c5a612
"  -f, --file <filename>		Read input from <filename>\n"
Packit c5a612
"  -i, --interactive		Read input from interactive CLI\n"
Packit c5a612
"\n"
Packit c5a612
"  -j, --json			Format output in JSON\n"
Packit c5a612
"  -n, --numeric			Print fully numerical output.\n"
Packit c5a612
"  -s, --stateless		Omit stateful information of ruleset.\n"
Packit c5a612
"  -t, --terse			Omit contents of sets.\n"
Packit c5a612
"  -u, --guid			Print UID/GID as defined in /etc/passwd and /etc/group.\n"
Packit c5a612
"  -N				Translate IP addresses to names.\n"
Packit c5a612
"  -S, --service			Translate ports to service names as described in /etc/services.\n"
Packit c5a612
"  -p, --numeric-protocol	Print layer 4 protocols numerically.\n"
Packit c5a612
"  -y, --numeric-priority	Print chain priority numerically.\n"
Packit c5a612
"  -T, --numeric-time		Print time values numerically.\n"
Packit c5a612
"  -a, --handle			Output rule handle.\n"
Packit c5a612
"  -e, --echo			Echo what has been added, inserted or replaced.\n"
Packit c5a612
"  -I, --includepath <directory>	Add <directory> to the paths searched for include files. Default is: %s\n"
Packit c5a612
"  --debug <level [,level...]>	Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)\n"
Packit c5a612
"\n",
Packit c5a612
	name, DEFAULT_INCLUDE_PATH);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct {
Packit c5a612
	const char		*name;
Packit c5a612
	enum nft_debug_level	level;
Packit c5a612
} debug_param[] = {
Packit c5a612
	{
Packit c5a612
		.name		= "scanner",
Packit c5a612
		.level		= NFT_DEBUG_SCANNER,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "parser",
Packit c5a612
		.level		= NFT_DEBUG_PARSER,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "eval",
Packit c5a612
		.level		= NFT_DEBUG_EVALUATION,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "netlink",
Packit c5a612
		.level		= NFT_DEBUG_NETLINK,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "mnl",
Packit c5a612
		.level		= NFT_DEBUG_MNL,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "proto-ctx",
Packit c5a612
		.level		= NFT_DEBUG_PROTO_CTX,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "segtree",
Packit c5a612
		.level		= NFT_DEBUG_SEGTREE,
Packit c5a612
	},
Packit c5a612
	{
Packit c5a612
		.name		= "all",
Packit c5a612
		.level		= ~0,
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit Service 095fa0
static void nft_options_error(int argc, char * const argv[], int pos)
Packit Service 095fa0
{
Packit Service 095fa0
	int i;
Packit Service 095fa0
Packit Service 095fa0
	fprintf(stderr, "Error: syntax error, options must be specified before commands\n");
Packit Service 095fa0
	for (i = 0; i < argc; i++)
Packit Service 095fa0
		fprintf(stderr, "%s ", argv[i]);
Packit Service 095fa0
	printf("\n%4c%*s\n", '^', pos - 2, "~~");
Packit Service 095fa0
}
Packit Service 095fa0
Packit Service 095fa0
static bool nft_options_check(int argc, char * const argv[])
Packit Service 095fa0
{
Packit Service 095fa0
	bool skip = false, nonoption = false;
Packit Service 095fa0
	int pos = 0, i;
Packit Service 095fa0
Packit Service 095fa0
	for (i = 1; i < argc; i++) {
Packit Service 095fa0
		pos += strlen(argv[i - 1]) + 1;
Packit Service 095fa0
		if (argv[i][0] == '{') {
Packit Service 095fa0
			break;
Packit Service 095fa0
		} else if (skip) {
Packit Service 095fa0
			skip = false;
Packit Service 095fa0
			continue;
Packit Service 095fa0
		} else if (argv[i][0] == '-') {
Packit Service 095fa0
			if (nonoption) {
Packit Service 095fa0
				nft_options_error(argc, argv, pos);
Packit Service 095fa0
				return false;
Packit Service 1894c6
			} else if (argv[i][1] == 'd' ||
Packit Service 1894c6
				   argv[i][1] == 'I' ||
Packit Service 095fa0
				   argv[i][1] == 'f' ||
Packit Service 1894c6
				   !strcmp(argv[i], "--debug") ||
Packit Service 095fa0
				   !strcmp(argv[i], "--includepath") ||
Packit Service 095fa0
				   !strcmp(argv[i], "--file")) {
Packit Service 095fa0
				skip = true;
Packit Service 095fa0
				continue;
Packit Service 095fa0
			}
Packit Service 095fa0
		} else if (argv[i][0] != '-') {
Packit Service 095fa0
			nonoption = true;
Packit Service 095fa0
		}
Packit Service 095fa0
	}
Packit Service 095fa0
Packit Service 095fa0
	return true;
Packit Service 095fa0
}
Packit Service 095fa0
Packit c5a612
int main(int argc, char * const *argv)
Packit c5a612
{
Packit c5a612
	char *buf = NULL, *filename = NULL;
Packit c5a612
	unsigned int output_flags = 0;
Packit c5a612
	bool interactive = false;
Packit c5a612
	unsigned int debug_mask;
Packit c5a612
	unsigned int len;
Packit c5a612
	int i, val, rc;
Packit c5a612
Packit Service 095fa0
	if (!nft_options_check(argc, argv))
Packit Service 095fa0
		exit(EXIT_FAILURE);
Packit Service 095fa0
Packit c5a612
	nft = nft_ctx_new(NFT_CTX_DEFAULT);
Packit c5a612
Packit c5a612
	while (1) {
Packit c5a612
		val = getopt_long(argc, argv, OPTSTRING, options, NULL);
Packit c5a612
		if (val == -1)
Packit c5a612
			break;
Packit c5a612
Packit c5a612
		switch (val) {
Packit c5a612
		case OPT_HELP:
Packit c5a612
			show_help(argv[0]);
Packit c5a612
			exit(EXIT_SUCCESS);
Packit c5a612
		case OPT_VERSION:
Packit c5a612
			printf("%s v%s (%s)\n",
Packit c5a612
			       PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME);
Packit c5a612
			exit(EXIT_SUCCESS);
Packit c5a612
		case OPT_CHECK:
Packit c5a612
			nft_ctx_set_dry_run(nft, true);
Packit c5a612
			break;
Packit c5a612
		case OPT_FILE:
Packit c5a612
			filename = optarg;
Packit c5a612
			break;
Packit c5a612
		case OPT_INTERACTIVE:
Packit c5a612
			interactive = true;
Packit c5a612
			break;
Packit c5a612
		case OPT_INCLUDEPATH:
Packit c5a612
			if (nft_ctx_add_include_path(nft, optarg)) {
Packit c5a612
				fprintf(stderr,
Packit c5a612
					"Failed to add include path '%s'\n",
Packit c5a612
					optarg);
Packit c5a612
				exit(EXIT_FAILURE);
Packit c5a612
			}
Packit c5a612
			break;
Packit c5a612
		case OPT_NUMERIC:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_NUMERIC_ALL;
Packit c5a612
			break;
Packit c5a612
		case OPT_STATELESS:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_STATELESS;
Packit c5a612
			break;
Packit c5a612
		case OPT_IP2NAME:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_REVERSEDNS;
Packit c5a612
			break;
Packit c5a612
		case OPT_SERVICE:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_SERVICE;
Packit c5a612
			break;
Packit c5a612
		case OPT_DEBUG:
Packit c5a612
			debug_mask = nft_ctx_output_get_debug(nft);
Packit c5a612
			for (;;) {
Packit c5a612
				unsigned int i;
Packit c5a612
				char *end;
Packit c5a612
Packit c5a612
				end = strchr(optarg, ',');
Packit c5a612
				if (end)
Packit c5a612
					*end = '\0';
Packit c5a612
Packit c5a612
				for (i = 0; i < array_size(debug_param); i++) {
Packit c5a612
					if (strcmp(debug_param[i].name, optarg))
Packit c5a612
						continue;
Packit c5a612
					debug_mask |= debug_param[i].level;
Packit c5a612
					break;
Packit c5a612
				}
Packit c5a612
Packit c5a612
				if (i == array_size(debug_param)) {
Packit c5a612
					fprintf(stderr, "invalid debug parameter `%s'\n",
Packit c5a612
						optarg);
Packit c5a612
					exit(EXIT_FAILURE);
Packit c5a612
				}
Packit c5a612
Packit c5a612
				if (end == NULL)
Packit c5a612
					break;
Packit c5a612
				optarg = end + 1;
Packit c5a612
			}
Packit c5a612
			nft_ctx_output_set_debug(nft, debug_mask);
Packit c5a612
			break;
Packit c5a612
		case OPT_HANDLE_OUTPUT:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_HANDLE;
Packit c5a612
			break;
Packit c5a612
		case OPT_ECHO:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_ECHO;
Packit c5a612
			break;
Packit c5a612
		case OPT_JSON:
Packit c5a612
#ifdef HAVE_LIBJANSSON
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_JSON;
Packit c5a612
#else
Packit c5a612
			fprintf(stderr, "JSON support not compiled-in\n");
Packit c5a612
			exit(EXIT_FAILURE);
Packit c5a612
#endif
Packit c5a612
			break;
Packit c5a612
		case OPT_GUID:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_GUID;
Packit c5a612
			break;
Packit c5a612
		case OPT_NUMERIC_PRIO:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_NUMERIC_PRIO;
Packit c5a612
			break;
Packit c5a612
		case OPT_NUMERIC_PROTO:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_NUMERIC_PROTO;
Packit c5a612
			break;
Packit c5a612
		case OPT_NUMERIC_TIME:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_NUMERIC_TIME;
Packit c5a612
			break;
Packit c5a612
		case OPT_TERSE:
Packit c5a612
			output_flags |= NFT_CTX_OUTPUT_TERSE;
Packit c5a612
			break;
Packit c5a612
		case OPT_INVALID:
Packit c5a612
			exit(EXIT_FAILURE);
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
Packit c5a612
	nft_ctx_output_set_flags(nft, output_flags);
Packit c5a612
Packit c5a612
	if (optind != argc) {
Packit c5a612
		for (len = 0, i = optind; i < argc; i++)
Packit c5a612
			len += strlen(argv[i]) + strlen(" ");
Packit c5a612
Packit c5a612
		buf = calloc(1, len);
Packit c5a612
		if (buf == NULL) {
Packit c5a612
			fprintf(stderr, "%s:%u: Memory allocation failure\n",
Packit c5a612
				__FILE__, __LINE__);
Packit c5a612
			exit(EXIT_FAILURE);
Packit c5a612
		}
Packit c5a612
		for (i = optind; i < argc; i++) {
Packit c5a612
			strcat(buf, argv[i]);
Packit c5a612
			if (i + 1 < argc)
Packit c5a612
				strcat(buf, " ");
Packit c5a612
		}
Packit c5a612
		rc = !!nft_run_cmd_from_buffer(nft, buf);
Packit c5a612
	} else if (filename != NULL) {
Packit c5a612
		rc = !!nft_run_cmd_from_filename(nft, filename);
Packit c5a612
	} else if (interactive) {
Packit c5a612
		if (cli_init(nft) < 0) {
Packit c5a612
			fprintf(stderr, "%s: interactive CLI not supported in this build\n",
Packit c5a612
				argv[0]);
Packit c5a612
			exit(EXIT_FAILURE);
Packit c5a612
		}
Packit c5a612
		return EXIT_SUCCESS;
Packit c5a612
	} else {
Packit c5a612
		fprintf(stderr, "%s: no command specified\n", argv[0]);
Packit c5a612
		exit(EXIT_FAILURE);
Packit c5a612
	}
Packit c5a612
Packit c5a612
	free(buf);
Packit c5a612
	nft_ctx_free(nft);
Packit c5a612
Packit c5a612
	return rc;
Packit c5a612
}