Example #1
0
struct option *xtables_merge_options(struct option *oldopts,
				     const struct option *newopts,
				     unsigned int *option_offset)
{
	unsigned int num_old, num_new, i;
	struct option *merge;

	if (newopts == NULL)
		return oldopts;

	for (num_old = 0; oldopts[num_old].name; num_old++) ;
	for (num_new = 0; newopts[num_new].name; num_new++) ;

	xt_params->option_offset += 256;
	*option_offset = xt_params->option_offset;

	merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
	if (merge == NULL)
		return NULL;
	memcpy(merge, oldopts, num_old * sizeof(struct option));
	xtables_free_opts(0);	/* Release any old options merged  */
	for (i = 0; i < num_new; i++) {
		merge[num_old + i] = newopts[i];
		merge[num_old + i].val += *option_offset;
	}
	memset(merge + num_old + num_new, 0, sizeof(struct option));

	return merge;
}
exit_tryhelp(int status)
{
	if (line != -1)
		fprintf(stderr, "Error occurred at line: %d\n", line);
	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
			prog_name, prog_name);
	xtables_free_opts(1);
	exit(status);
}
Example #3
0
void get_kernel_version(void)
{
	static struct utsname uts;
	int x = 0, y = 0, z = 0;

	if (uname(&uts) == -1) {
		fprintf(stderr, "Unable to retrieve kernel version.\n");
		xtables_free_opts(1);
		exit(1);
	}

	sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
	kernel_version = LINUX_VERSION(x, y, z);
}
Example #4
0
struct option *xtables_merge_options(struct option *orig_opts,
				     struct option *oldopts,
				     const struct option *newopts,
				     unsigned int *option_offset)
{
	unsigned int num_oold = 0, num_old = 0, num_new = 0, i;
	struct option *merge, *mp;

	if (newopts == NULL)
		return oldopts;

	for (num_oold = 0; orig_opts[num_oold].name; num_oold++) ;
	if (oldopts != NULL)
		for (num_old = 0; oldopts[num_old].name; num_old++) ;
	for (num_new = 0; newopts[num_new].name; num_new++) ;

	/*
	 * Since @oldopts also has @orig_opts already (and does so at the
	 * start), skip these entries.
	 */
	oldopts += num_oold;
	num_old -= num_oold;

	merge = malloc(sizeof(*mp) * (num_oold + num_old + num_new + 1));
	if (merge == NULL)
		return NULL;

	/* Let the base options -[ADI...] have precedence over everything */
	memcpy(merge, orig_opts, sizeof(*mp) * num_oold);
	mp = merge + num_oold;

	/* Second, the new options */
	xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
	*option_offset = xt_params->option_offset;
	memcpy(mp, newopts, sizeof(*mp) * num_new);

	for (i = 0; i < num_new; ++i, ++mp)
		mp->val += *option_offset;

	/* Third, the old options */
	memcpy(mp, oldopts, sizeof(*mp) * num_old);
	mp += num_old;
	xtables_free_opts(0);

	/* Clear trailing entry */
	memset(mp, 0, sizeof(*mp));
	return merge;
}
void
xtables_exit_error(enum xtables_exittype status, const char *msg, ...)
{
	va_list args;

	va_start(args, msg);
	fprintf(stderr, "%s v%s: ", prog_name, prog_vers);
	vfprintf(stderr, msg, args);
	va_end(args);
	fprintf(stderr, "\n");
	if (status == PARAMETER_PROBLEM)
		exit_tryhelp(status);
	if (status == VERSION_PROBLEM)
		fprintf(stderr,
			"Perhaps iptables or your kernel needs to be upgraded.\n");
	/* On error paths, make sure that we don't leak memory */
	xtables_free_opts(1);
	exit(status);
}
Example #6
0
static int parse_ipt(struct action_util *a,int *argc_p,
		     char ***argv_p, int tca_id, struct nlmsghdr *n)
{
	struct xtables_target *m = NULL;
	struct ipt_entry fw;
	struct rtattr *tail;

	int c;
	int rargc = *argc_p;
	char **argv = *argv_p;
	int argc = 0, iargc = 0;
	char k[16];
	int size = 0;
	int iok = 0, ok = 0;
	__u32 hook = 0, index = 0;
	struct option *opts = NULL;

	xtables_init_all(&tcipt_globals, NFPROTO_IPV4);
	set_lib_dir();

	{
		int i;
		for (i = 0; i < rargc; i++) {
			if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) {
				break;
			}
		}
		iargc = argc = i;
	}

	if (argc <= 2) {
		fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc);
		return -1;
	}

	while (1) {
		c = getopt_long(argc, argv, "j:", tcipt_globals.opts, NULL);
		if (c == -1)
			break;
		switch (c) {
		case 'j':
			m = xtables_find_target(optarg, XTF_TRY_LOAD);
			if (NULL != m) {

				if (0 > build_st(m, NULL)) {
					printf(" %s error \n", m->name);
					return -1;
				}
#if (XTABLES_VERSION_CODE >= 6)
			opts = xtables_options_xfrm(tcipt_globals.orig_opts,
						    tcipt_globals.opts,
						    m->x6_options,
						    &m->option_offset);
#else
			opts = xtables_merge_options(tcipt_globals.orig_opts,
						     tcipt_globals.opts,
						     m->extra_opts,
						     &m->option_offset);
#endif
			if (opts == NULL) {
				fprintf(stderr, " failed to find aditional options for target %s\n\n", optarg);
				return -1;
			} else
				tcipt_globals.opts = opts;
			} else {
				fprintf(stderr," failed to find target %s\n\n", optarg);
				return -1;
			}
			ok++;
			break;

		default:
			memset(&fw, 0, sizeof (fw));
#if (XTABLES_VERSION_CODE >= 6)
		if (m != NULL && m->x6_parse != NULL ) {
			xtables_option_tpcall(c, argv, 0 , m, NULL);
#else
		if (m != NULL && m->parse != NULL ) {
			m->parse(c - m->option_offset, argv, 0, &m->tflags,
				 NULL, &m->t);
#endif
			} else {
				fprintf(stderr,"failed to find target %s\n\n", optarg);
				return -1;

			}
			ok++;
			break;
		}
	}

	if (iargc > optind) {
		if (matches(argv[optind], "index") == 0) {
			if (get_u32(&index, argv[optind + 1], 10)) {
				fprintf(stderr, "Illegal \"index\"\n");
				xtables_free_opts(1);
				return -1;
			}
			iok++;

			optind += 2;
		}
	}

	if (!ok && !iok) {
		fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv);
		return -1;
	}

	/* check that we passed the correct parameters to the target */
#if (XTABLES_VERSION_CODE >= 6)
	if (m)
		xtables_option_tfcall(m);
#else
	if (m && m->final_check)
		m->final_check(m->tflags);
#endif

	{
		struct tcmsg *t = NLMSG_DATA(n);
		if (t->tcm_parent != TC_H_ROOT
		    && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
			hook = NF_IP_PRE_ROUTING;
		} else {
			hook = NF_IP_POST_ROUTING;
		}
	}

	tail = NLMSG_TAIL(n);
	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
	fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
	fprintf(stdout, "\ttarget: ");

	if (m)
		m->print(NULL, m->t, 0);
	fprintf(stdout, " index %d\n", index);

	if (strlen(tname) > 16) {
		size = 16;
		k[15] = 0;
	} else {
		size = 1 + strlen(tname);
	}
	strncpy(k, tname, size);

	addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
	addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
	addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
	if (m)
		addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;

	argc -= optind;
	argv += optind;
	*argc_p = rargc - iargc;
	*argv_p = argv;

	optind = 0;
	xtables_free_opts(1);

	if (m) {
		/* Clear flags if target will be used again */
		m->tflags = 0;
		m->used = 0;
		/* Free allocated memory */
		if (m->t)
			free(m->t);
	}

	return 0;

}

static int
print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
{
	struct rtattr *tb[TCA_IPT_MAX + 1];
	struct xt_entry_target *t = NULL;
	struct option *opts = NULL;

	if (arg == NULL)
		return -1;

	xtables_init_all(&tcipt_globals, NFPROTO_IPV4);
	set_lib_dir();

	parse_rtattr_nested(tb, TCA_IPT_MAX, arg);

	if (tb[TCA_IPT_TABLE] == NULL) {
		fprintf(f, "[NULL ipt table name ] assuming mangle ");
	} else {
		fprintf(f, "tablename: %s ",
			rta_getattr_str(tb[TCA_IPT_TABLE]));
	}

	if (tb[TCA_IPT_HOOK] == NULL) {
		fprintf(f, "[NULL ipt hook name ]\n ");
		return -1;
	} else {
		__u32 hook;
		hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
		fprintf(f, " hook: %s \n", ipthooks[hook]);
	}

	if (tb[TCA_IPT_TARG] == NULL) {
		fprintf(f, "\t[NULL ipt target parameters ] \n");
		return -1;
	} else {
		struct xtables_target *m = NULL;
		t = RTA_DATA(tb[TCA_IPT_TARG]);
		m = xtables_find_target(t->u.user.name, XTF_TRY_LOAD);
		if (NULL != m) {
			if (0 > build_st(m, t)) {
				fprintf(stderr, " %s error \n", m->name);
				return -1;
			}

#if (XTABLES_VERSION_CODE >= 6)
		opts = xtables_options_xfrm(tcipt_globals.orig_opts,
					    tcipt_globals.opts,
					    m->x6_options,
					    &m->option_offset);
#else
		opts = xtables_merge_options(tcipt_globals.orig_opts,
					     tcipt_globals.opts,
					     m->extra_opts,
					     &m->option_offset);
#endif
	if (opts == NULL) {
		fprintf(stderr, " failed to find aditional options for target %s\n\n", optarg);
		return -1;
	} else
		tcipt_globals.opts = opts;
		} else {
			fprintf(stderr, " failed to find target %s\n\n",
				t->u.user.name);
			return -1;
		}
		fprintf(f, "\ttarget ");
		m->print(NULL, m->t, 0);
		if (tb[TCA_IPT_INDEX] == NULL) {
			fprintf(f, " [NULL ipt target index ]\n");
		} else {
			__u32 index;
			index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
			fprintf(f, " \n\tindex %d", index);
		}

		if (tb[TCA_IPT_CNT]) {
			struct tc_cnt *c  = RTA_DATA(tb[TCA_IPT_CNT]);;
			fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
		}
		if (show_stats) {
			if (tb[TCA_IPT_TM]) {
				struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
				print_tm(f,tm);
			}
		}
		fprintf(f, " \n");

	}
	xtables_free_opts(1);

	return 0;
}

struct action_util xt_action_util = {
        .id = "xt",
        .parse_aopt = parse_ipt,
        .print_aopt = print_ipt,
};
Example #7
0
static int
print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
{
	struct rtattr *tb[TCA_IPT_MAX + 1];
	struct xt_entry_target *t = NULL;

	if (arg == NULL)
		return -1;

	xtables_init_all(&tcipt_globals, NFPROTO_IPV4);
	set_lib_dir();

	parse_rtattr_nested(tb, TCA_IPT_MAX, arg);

	if (tb[TCA_IPT_TABLE] == NULL) {
		fprintf(f, "[NULL ipt table name ] assuming mangle ");
	} else {
		fprintf(f, "tablename: %s ",
			rta_getattr_str(tb[TCA_IPT_TABLE]));
	}

	if (tb[TCA_IPT_HOOK] == NULL) {
		fprintf(f, "[NULL ipt hook name ]\n ");
		return -1;
	} else {
		__u32 hook;
		hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
		fprintf(f, " hook: %s \n", ipthooks[hook]);
	}

	if (tb[TCA_IPT_TARG] == NULL) {
		fprintf(f, "\t[NULL ipt target parameters ] \n");
		return -1;
	} else {
		struct xtables_target *m = NULL;
		t = RTA_DATA(tb[TCA_IPT_TARG]);
		m = xtables_find_target(t->u.user.name, XTF_TRY_LOAD);
		if (NULL != m) {
			if (0 > build_st(m, t)) {
				fprintf(stderr, " %s error \n", m->name);
				return -1;
			}

			tcipt_globals.opts =
			    xtables_merge_options(
#if (XTABLES_VERSION_CODE >= 6)
				                  tcipt_globals.orig_opts,
#endif
				                  tcipt_globals.opts,
			                          m->extra_opts,
			                          &m->option_offset);
		} else {
			fprintf(stderr, " failed to find target %s\n\n",
				t->u.user.name);
			return -1;
		}
		fprintf(f, "\ttarget ");
		m->print(NULL, m->t, 0);
		if (tb[TCA_IPT_INDEX] == NULL) {
			fprintf(f, " [NULL ipt target index ]\n");
		} else {
			__u32 index;
			index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
			fprintf(f, " \n\tindex %d", index);
		}

		if (tb[TCA_IPT_CNT]) {
			struct tc_cnt *c  = RTA_DATA(tb[TCA_IPT_CNT]);;
			fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
		}
		if (show_stats) {
			if (tb[TCA_IPT_TM]) {
				struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
				print_tm(f,tm);
			}
		}
		fprintf(f, " \n");

	}
	xtables_free_opts(1);

	return 0;
}
Example #8
0
static int parse_ipt(struct action_util *a,int *argc_p,
		     char ***argv_p, int tca_id, struct nlmsghdr *n)
{
	struct xtables_target *m = NULL;
	struct ipt_entry fw;
	struct rtattr *tail;
	int c;
	int rargc = *argc_p;
	char **argv = *argv_p;
	int argc = 0, iargc = 0;
	char k[16];
	int size = 0;
	int iok = 0, ok = 0;
	__u32 hook = 0, index = 0;

	xtables_init_all(&tcipt_globals, NFPROTO_IPV4);
	set_lib_dir();

	{
		int i;
		for (i = 0; i < rargc; i++) {
			if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) {
				break;
			}
		}
		iargc = argc = i;
	}

	if (argc <= 2) {
		fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc);
		return -1;
	}

	while (1) {
		c = getopt_long(argc, argv, "j:", tcipt_globals.opts, NULL);
		if (c == -1)
			break;
		switch (c) {
		case 'j':
			m = xtables_find_target(optarg, XTF_TRY_LOAD);
			if (NULL != m) {

				if (0 > build_st(m, NULL)) {
					printf(" %s error \n", m->name);
					return -1;
				}
				tcipt_globals.opts =
				    xtables_merge_options(
#if (XTABLES_VERSION_CODE >= 6)
				        tcipt_globals.orig_opts,
#endif
				        tcipt_globals.opts,
				        m->extra_opts,
				        &m->option_offset);
			} else {
				fprintf(stderr," failed to find target %s\n\n", optarg);
				return -1;
			}
			ok++;
			break;

		default:
			memset(&fw, 0, sizeof (fw));
			if (m) {
				m->parse(c - m->option_offset, argv, 0,
					 &m->tflags, NULL, &m->t);
			} else {
				fprintf(stderr," failed to find target %s\n\n", optarg);
				return -1;

			}
			ok++;
			break;

		}
	}

	if (iargc > optind) {
		if (matches(argv[optind], "index") == 0) {
			if (get_u32(&index, argv[optind + 1], 10)) {
				fprintf(stderr, "Illegal \"index\"\n");
				xtables_free_opts(1);
				return -1;
			}
			iok++;

			optind += 2;
		}
	}

	if (!ok && !iok) {
		fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv);
		return -1;
	}

	
	if (m && m->final_check)
		m->final_check(m->tflags);

	{
		struct tcmsg *t = NLMSG_DATA(n);
		if (t->tcm_parent != TC_H_ROOT
		    && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
			hook = NF_IP_PRE_ROUTING;
		} else {
			hook = NF_IP_POST_ROUTING;
		}
	}

	tail = NLMSG_TAIL(n);
	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
	fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
	fprintf(stdout, "\ttarget: ");

	if (m)
		m->print(NULL, m->t, 0);
	fprintf(stdout, " index %d\n", index);

	if (strlen(tname) > 16) {
		size = 16;
		k[15] = 0;
	} else {
		size = 1 + strlen(tname);
	}
	strncpy(k, tname, size);

	addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
	addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
	addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
	if (m)
		addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;

	argc -= optind;
	argv += optind;
	*argc_p = rargc - iargc;
	*argv_p = argv;

	optind = 0;
	xtables_free_opts(1);

	if (m) {
		
		m->tflags = 0;
		m->used = 0;
		
		if (m->t)
			free(m->t);
	}

	return 0;

}
int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
		bool restore)
{
	struct iptables_command_state cs;
	int verbose = 0;
	const char *chain = NULL;
	const char *policy = NULL, *newname = NULL;
	unsigned int rulenum = 0, command = 0;
	int ret = 1;
	struct xtables_match *m;
	struct xtables_rule_match *matchp;
	struct xtables_target *t;
	struct xtables_args args = {
		.family	= h->family,
	};

	memset(&cs, 0, sizeof(cs));
	cs.jumpto = "";
	cs.argv = argv;

	/* re-set optind to 0 in case do_command4 gets called
	 * a second time */
	optind = 0;

	/* clear mflags in case do_command4 gets called a second time
	 * (we clear the global list of all matches for security)*/
	for (m = xtables_matches; m; m = m->next)
		m->mflags = 0;

	for (t = xtables_targets; t; t = t->next) {
		t->tflags = 0;
		t->used = 0;
	}

	/* Suppress error messages: we may add new options if we
	   demand-load a protocol. */
	opterr = 0;

	h->ops = nft_family_ops_lookup(h->family);
	if (h->ops == NULL)
		xtables_error(PARAMETER_PROBLEM, "Unknown family");

	opts = xt_params->orig_opts;
	while ((cs.c = getopt_long(argc, argv,
	   "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:46",
					   opts, NULL)) != -1) {
		switch (cs.c) {
			/*
			 * Command selection
			 */
		case 'A':
			add_command(&command, CMD_APPEND, CMD_NONE,
				    cs.invert);
			chain = optarg;
			break;

		case 'C':
			add_command(&command, CMD_CHECK, CMD_NONE,
				    cs.invert);
			chain = optarg;
			break;

		case 'D':
			add_command(&command, CMD_DELETE, CMD_NONE,
				    cs.invert);
			chain = optarg;
			if (optind < argc && argv[optind][0] != '-'
			    && argv[optind][0] != '!') {
				rulenum = parse_rulenumber(argv[optind++]);
				command = CMD_DELETE_NUM;
			}
			break;

		case 'R':
			add_command(&command, CMD_REPLACE, CMD_NONE,
				    cs.invert);
			chain = optarg;
			if (optind < argc && argv[optind][0] != '-'
			    && argv[optind][0] != '!')
				rulenum = parse_rulenumber(argv[optind++]);
			else
				xtables_error(PARAMETER_PROBLEM,
					   "-%c requires a rule number",
					   cmd2char(CMD_REPLACE));
			break;

		case 'I':
			add_command(&command, CMD_INSERT, CMD_NONE,
				    cs.invert);
			chain = optarg;
			if (optind < argc && argv[optind][0] != '-'
			    && argv[optind][0] != '!')
				rulenum = parse_rulenumber(argv[optind++]);
			else rulenum = 1;
			break;

		case 'L':
			add_command(&command, CMD_LIST,
				    CMD_ZERO | CMD_ZERO_NUM, cs.invert);
			if (optarg) chain = optarg;
			else if (optind < argc && argv[optind][0] != '-'
				 && argv[optind][0] != '!')
				chain = argv[optind++];
			if (optind < argc && argv[optind][0] != '-'
			    && argv[optind][0] != '!')
				rulenum = parse_rulenumber(argv[optind++]);
			break;

		case 'S':
			add_command(&command, CMD_LIST_RULES,
				    CMD_ZERO|CMD_ZERO_NUM, cs.invert);
			if (optarg) chain = optarg;
			else if (optind < argc && argv[optind][0] != '-'
				 && argv[optind][0] != '!')
				chain = argv[optind++];
			if (optind < argc && argv[optind][0] != '-'
			    && argv[optind][0] != '!')
				rulenum = parse_rulenumber(argv[optind++]);
			break;

		case 'F':
			add_command(&command, CMD_FLUSH, CMD_NONE,
				    cs.invert);
			if (optarg) chain = optarg;
			else if (optind < argc && argv[optind][0] != '-'
				 && argv[optind][0] != '!')
				chain = argv[optind++];
			break;

		case 'Z':
			add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES,
				    cs.invert);
			if (optarg) chain = optarg;
			else if (optind < argc && argv[optind][0] != '-'
				&& argv[optind][0] != '!')
				chain = argv[optind++];
			if (optind < argc && argv[optind][0] != '-'
				&& argv[optind][0] != '!') {
				rulenum = parse_rulenumber(argv[optind++]);
				command = CMD_ZERO_NUM;
			}
			break;

		case 'N':
			if (optarg && (*optarg == '-' || *optarg == '!'))
				xtables_error(PARAMETER_PROBLEM,
					   "chain name not allowed to start "
					   "with `%c'\n", *optarg);
			if (xtables_find_target(optarg, XTF_TRY_LOAD))
				xtables_error(PARAMETER_PROBLEM,
					   "chain name may not clash "
					   "with target name\n");
			add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
				    cs.invert);
			chain = optarg;
			break;

		case 'X':
			add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
				    cs.invert);
			if (optarg) chain = optarg;
			else if (optind < argc && argv[optind][0] != '-'
				 && argv[optind][0] != '!')
				chain = argv[optind++];
			break;

		case 'E':
			add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
				    cs.invert);
			chain = optarg;
			if (optind < argc && argv[optind][0] != '-'
			    && argv[optind][0] != '!')
				newname = argv[optind++];
			else
				xtables_error(PARAMETER_PROBLEM,
					   "-%c requires old-chain-name and "
					   "new-chain-name",
					    cmd2char(CMD_RENAME_CHAIN));
			break;

		case 'P':
			add_command(&command, CMD_SET_POLICY, CMD_NONE,
				    cs.invert);
			chain = optarg;
			if (optind < argc && argv[optind][0] != '-'
			    && argv[optind][0] != '!')
				policy = argv[optind++];
			else
				xtables_error(PARAMETER_PROBLEM,
					   "-%c requires a chain and a policy",
					   cmd2char(CMD_SET_POLICY));
			break;

		case 'h':
			if (!optarg)
				optarg = argv[optind];

			/* iptables -p icmp -h */
			if (!cs.matches && cs.protocol)
				xtables_find_match(cs.protocol,
					XTF_TRY_LOAD, &cs.matches);

			exit_printhelp(cs.matches);

			/*
			 * Option selection
			 */
		case 'p':
			set_option(&cs.options, OPT_PROTOCOL, &args.invflags,
				   cs.invert);

			/* Canonicalize into lower case */
			for (cs.protocol = optarg; *cs.protocol; cs.protocol++)
				*cs.protocol = tolower(*cs.protocol);

			cs.protocol = optarg;
			args.proto = xtables_parse_protocol(cs.protocol);

			if (args.proto == 0 && (args.invflags & XT_INV_PROTO))
				xtables_error(PARAMETER_PROBLEM,
					   "rule would never match protocol");

			/* This needs to happen here to parse extensions */
			h->ops->proto_parse(&cs, &args);
			break;

		case 's':
			set_option(&cs.options, OPT_SOURCE, &args.invflags,
				   cs.invert);
			args.shostnetworkmask = optarg;
			break;

		case 'd':
			set_option(&cs.options, OPT_DESTINATION,
				   &args.invflags, cs.invert);
			args.dhostnetworkmask = optarg;
			break;

#ifdef IPT_F_GOTO
		case 'g':
			set_option(&cs.options, OPT_JUMP, &args.invflags,
				   cs.invert);
			args.goto_set = true;
			cs.jumpto = parse_target(optarg);
			break;
#endif

		case 'j':
			command_jump(&cs);
			break;


		case 'i':
			if (*optarg == '\0')
				xtables_error(PARAMETER_PROBLEM,
					"Empty interface is likely to be "
					"undesired");
			set_option(&cs.options, OPT_VIANAMEIN, &args.invflags,
				   cs.invert);
			xtables_parse_interface(optarg,
						args.iniface,
						args.iniface_mask);
			break;

		case 'o':
			if (*optarg == '\0')
				xtables_error(PARAMETER_PROBLEM,
					"Empty interface is likely to be "
					"undesired");
			set_option(&cs.options, OPT_VIANAMEOUT, &args.invflags,
				   cs.invert);
			xtables_parse_interface(optarg,
						args.outiface,
						args.outiface_mask);
			break;

		case 'f':
			if (args.family == AF_INET6) {
				xtables_error(PARAMETER_PROBLEM,
					"`-f' is not supported in IPv6, "
					"use -m frag instead");
			}
			set_option(&cs.options, OPT_FRAGMENT, &args.invflags,
				   cs.invert);
			args.flags |= IPT_F_FRAG;
			break;

		case 'v':
			if (!verbose)
				set_option(&cs.options, OPT_VERBOSE,
					   &args.invflags, cs.invert);
			verbose++;
			break;

		case 'm':
			command_match(&cs);
			break;

		case 'n':
			set_option(&cs.options, OPT_NUMERIC, &args.invflags,
				   cs.invert);
			break;

		case 't':
			if (cs.invert)
				xtables_error(PARAMETER_PROBLEM,
					   "unexpected ! flag before --table");
			*table = optarg;
			break;

		case 'x':
			set_option(&cs.options, OPT_EXPANDED, &args.invflags,
				   cs.invert);
			break;

		case 'V':
			if (cs.invert)
				printf("Not %s ;-)\n", prog_vers);
			else
				printf("%s v%s\n",
				       prog_name, prog_vers);
			exit(0);

		case 'w':
			if (restore) {
				xtables_error(PARAMETER_PROBLEM,
					      "You cannot use `-w' from "
					      "iptables-restore");
			}
			break;

		case '0':
			set_option(&cs.options, OPT_LINENUMBERS,
				   &args.invflags, cs.invert);
			break;

		case 'M':
			xtables_modprobe_program = optarg;
			break;

		case 'c':

			set_option(&cs.options, OPT_COUNTERS, &args.invflags,
				   cs.invert);
			args.pcnt = optarg;
			args.bcnt = strchr(args.pcnt + 1, ',');
			if (args.bcnt)
			    args.bcnt++;
			if (!args.bcnt && optind < argc &&
			    argv[optind][0] != '-' &&
			    argv[optind][0] != '!')
				args.bcnt = argv[optind++];
			if (!args.bcnt)
				xtables_error(PARAMETER_PROBLEM,
					"-%c requires packet and byte counter",
					opt2char(OPT_COUNTERS));

			if (sscanf(args.pcnt, "%llu", &args.pcnt_cnt) != 1)
				xtables_error(PARAMETER_PROBLEM,
					"-%c packet counter not numeric",
					opt2char(OPT_COUNTERS));

			if (sscanf(args.bcnt, "%llu", &args.bcnt_cnt) != 1)
				xtables_error(PARAMETER_PROBLEM,
					"-%c byte counter not numeric",
					opt2char(OPT_COUNTERS));
			break;

		case '4':
			if (args.family != AF_INET)
				exit_tryhelp(2);

			h->ops = nft_family_ops_lookup(args.family);
			break;

		case '6':
			args.family = AF_INET6;
			xtables_set_nfproto(AF_INET6);

			h->ops = nft_family_ops_lookup(args.family);
			if (h->ops == NULL)
				xtables_error(PARAMETER_PROBLEM,
					      "Unknown family");
			break;

		case 1: /* non option */
			if (optarg[0] == '!' && optarg[1] == '\0') {
				if (cs.invert)
					xtables_error(PARAMETER_PROBLEM,
						   "multiple consecutive ! not"
						   " allowed");
				cs.invert = TRUE;
				optarg[0] = '\0';
				continue;
			}
			fprintf(stderr, "Bad argument `%s'\n", optarg);
			exit_tryhelp(2);

		default:
			if (command_default(&cs, &xtables_globals) == 1)
				/* cf. ip6tables.c */
				continue;
			break;
		}
		cs.invert = FALSE;
	}

	if (strcmp(*table, "nat") == 0 &&
	    ((policy != NULL && strcmp(policy, "DROP") == 0) ||
	    (cs.jumpto != NULL && strcmp(cs.jumpto, "DROP") == 0)))
		xtables_error(PARAMETER_PROBLEM,
			"\nThe \"nat\" table is not intended for filtering, "
			"the use of DROP is therefore inhibited.\n\n");

	for (matchp = cs.matches; matchp; matchp = matchp->next)
		xtables_option_mfcall(matchp->match);
	if (cs.target != NULL)
		xtables_option_tfcall(cs.target);

	/* Fix me: must put inverse options checking here --MN */

	if (optind < argc)
		xtables_error(PARAMETER_PROBLEM,
			   "unknown arguments found on commandline");
	if (!command)
		xtables_error(PARAMETER_PROBLEM, "no command specified");
	if (cs.invert)
		xtables_error(PARAMETER_PROBLEM,
			   "nothing appropriate following !");

	/* Set only if required, needed by xtables-restore */
	if (h->family == AF_UNSPEC)
		h->family = args.family;

	h->ops->post_parse(command, &cs, &args);

	if (command == CMD_REPLACE &&
	    (args.s.naddrs != 1 || args.d.naddrs != 1))
		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
			   "specify a unique address");

	generic_opt_check(command, cs.options);

	if (chain != NULL && strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
		xtables_error(PARAMETER_PROBLEM,
			   "chain name `%s' too long (must be under %u chars)",
			   chain, XT_EXTENSION_MAXNAMELEN);

	if (command == CMD_APPEND
	    || command == CMD_DELETE
	    || command == CMD_CHECK
	    || command == CMD_INSERT
	    || command == CMD_REPLACE) {
		if (strcmp(chain, "PREROUTING") == 0
		    || strcmp(chain, "INPUT") == 0) {
			/* -o not valid with incoming packets. */
			if (cs.options & OPT_VIANAMEOUT)
				xtables_error(PARAMETER_PROBLEM,
					   "Can't use -%c with %s\n",
					   opt2char(OPT_VIANAMEOUT),
					   chain);
		}

		if (strcmp(chain, "POSTROUTING") == 0
		    || strcmp(chain, "OUTPUT") == 0) {
			/* -i not valid with outgoing packets */
			if (cs.options & OPT_VIANAMEIN)
				xtables_error(PARAMETER_PROBLEM,
					   "Can't use -%c with %s\n",
					   opt2char(OPT_VIANAMEIN),
					   chain);
		}

		/*
		 * Contrary to what iptables does, we assume that any jumpto
		 * is a custom chain jumps (if no target is found). Later on,
		 * nf_table will spot the error if the chain does not exists.
		 */
	}

	switch (command) {
	case CMD_APPEND:
		ret = add_entry(chain, *table, &cs, 0, h->family,
				args.s, args.d, cs.options&OPT_VERBOSE,
				h, true);
		break;
	case CMD_DELETE:
		ret = delete_entry(chain, *table, &cs, h->family,
				   args.s, args.d, cs.options&OPT_VERBOSE, h);
		break;
	case CMD_DELETE_NUM:
		ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
		break;
	case CMD_CHECK:
		ret = check_entry(chain, *table, &cs, h->family,
				   args.s, args.d, cs.options&OPT_VERBOSE, h);
		break;
	case CMD_REPLACE:
		ret = replace_entry(chain, *table, &cs, rulenum - 1,
				    h->family, args.s, args.d,
				    cs.options&OPT_VERBOSE, h);
		break;
	case CMD_INSERT:
		ret = add_entry(chain, *table, &cs, rulenum - 1, h->family,
				args.s, args.d, cs.options&OPT_VERBOSE, h,
				false);
		break;
	case CMD_FLUSH:
		ret = nft_rule_flush(h, chain, *table);
		break;
	case CMD_ZERO:
		ret = nft_chain_zero_counters(h, chain, *table);
		break;
	case CMD_ZERO_NUM:
		ret = nft_rule_zero_counters(h, chain, *table, rulenum - 1);
		break;
	case CMD_LIST:
	case CMD_LIST|CMD_ZERO:
	case CMD_LIST|CMD_ZERO_NUM:
		ret = list_entries(h, chain, *table,
				   rulenum,
				   cs.options&OPT_VERBOSE,
				   cs.options&OPT_NUMERIC,
				   cs.options&OPT_EXPANDED,
				   cs.options&OPT_LINENUMBERS);
		if (ret && (command & CMD_ZERO))
			ret = nft_chain_zero_counters(h, chain, *table);
		if (ret && (command & CMD_ZERO_NUM))
			ret = nft_rule_zero_counters(h, chain, *table,
						     rulenum - 1);
		break;
	case CMD_LIST_RULES:
	case CMD_LIST_RULES|CMD_ZERO:
	case CMD_LIST_RULES|CMD_ZERO_NUM:
		ret = list_rules(h, chain, *table, rulenum, cs.options&OPT_VERBOSE);
		if (ret && (command & CMD_ZERO))
			ret = nft_chain_zero_counters(h, chain, *table);
		if (ret && (command & CMD_ZERO_NUM))
			ret = nft_rule_zero_counters(h, chain, *table,
						     rulenum - 1);
		break;
	case CMD_NEW_CHAIN:
		ret = nft_chain_user_add(h, chain, *table);
		break;
	case CMD_DELETE_CHAIN:
		ret = nft_chain_user_del(h, chain, *table);
		break;
	case CMD_RENAME_CHAIN:
		ret = nft_chain_user_rename(h, chain, *table, newname);
		break;
	case CMD_SET_POLICY:
		ret = nft_chain_set(h, *table, chain, policy, NULL);
		if (ret < 0)
			xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
				      policy);
		break;
	default:
		/* We should never reach this... */
		exit_tryhelp(2);
	}

/*	if (verbose > 1)
		dump_entries(*handle); */

	xtables_rule_matches_free(&cs.matches);

	if (h->family == AF_INET) {
		free(args.s.addr.v4);
		free(args.s.mask.v4);
		free(args.d.addr.v4);
		free(args.d.mask.v4);
	} else if (h->family == AF_INET6) {
		free(args.s.addr.v6);
		free(args.s.mask.v6);
		free(args.d.addr.v6);
		free(args.d.mask.v6);
	}
	xtables_free_opts(1);

	return ret;
}