Example #1
0
static int
brip_parse(int c, char **argv, int invert, unsigned int *flags,
	   const void *entry, struct xt_entry_match **match)
{
	struct ebt_ip_info *info = (struct ebt_ip_info *)(*match)->data;

	switch (c) {
	case IP_SOURCE:
		if (invert)
			info->invflags |= EBT_IP_SOURCE;
		ebt_parse_ip_address(optarg, &info->saddr, &info->smsk);
		info->bitmask |= EBT_IP_SOURCE;
		break;
	case IP_DEST:
		if (invert)
			info->invflags |= EBT_IP_DEST;
		ebt_parse_ip_address(optarg, &info->daddr, &info->dmsk);
		info->bitmask |= EBT_IP_DEST;
		break;
	case IP_SPORT:
		if (invert)
			info->invflags |= EBT_IP_SPORT;
		parse_port_range(NULL, optarg, info->sport);
		info->bitmask |= EBT_IP_SPORT;
		break;
	case IP_DPORT:
		if (invert)
			info->invflags |= EBT_IP_DPORT;
		parse_port_range(NULL, optarg, info->dport);
		info->bitmask |= EBT_IP_DPORT;
		break;
	case IP_EBT_TOS:
		if (invert)
			info->invflags |= EBT_IP_TOS;
		if (!xtables_strtoul(optarg, NULL, (uintmax_t *)&info->tos,
				     0, 255))
			xtables_error(PARAMETER_PROBLEM,
				      "Problem with specified IP tos");
		info->bitmask |= EBT_IP_TOS;
		break;
	case IP_PROTO:
		if (invert)
			info->invflags |= EBT_IP_PROTO;
		info->protocol = xtables_parse_protocol(optarg);
		if (info->protocol == -1)
			xtables_error(PARAMETER_PROBLEM,
				      "Unknown specified IP protocol - %s",
				      optarg);
		info->bitmask |= EBT_IP_PROTO;
		break;
	default:
		return 0;
	}

	*flags |= info->bitmask;
	return 1;
}
Example #2
0
/* Function which parses command options; returns true if it ate an option */
static int ipvs_mt_parse(int c, char **argv, int invert, unsigned int *flags,
			 const void *entry, struct xt_entry_match **match,
			 unsigned int family)
{
	struct xt_ipvs_mtinfo *data = (void *)(*match)->data;
	char *p = NULL;
	u_int8_t op = 0;

	if ('0' <= c && c <= '6') {
		static const int ops[] = {
			XT_IPVS_IPVS_PROPERTY,
			XT_IPVS_PROTO,
			XT_IPVS_VADDR,
			XT_IPVS_VPORT,
			XT_IPVS_DIR,
			XT_IPVS_METHOD,
			XT_IPVS_VPORTCTL
		};
		op = ops[c - '0'];
	} else
		return 0;

	if (*flags & op & XT_IPVS_ONCE_MASK)
		goto multiple_use;

	switch (c) {
	case '0': /* --ipvs */
		/* Nothing to do here. */
		break;

	case '1': /* --vproto */
		/* Canonicalize into lower case */
		for (p = optarg; *p != '\0'; ++p)
			*p = tolower(*p);

		data->l4proto = xtables_parse_protocol(optarg);
		break;

	case '2': /* --vaddr */
		ipvs_mt_parse_addr_and_mask(optarg, &data->vaddr,
					    &data->vmask, family);
		break;

	case '3': /* --vport */
		data->vport = htons(xtables_parse_port(optarg, "tcp"));
		break;

	case '4': /* --vdir */
		xtables_param_act(XTF_NO_INVERT, "ipvs", "--vdir", invert);
		if (strcasecmp(optarg, "ORIGINAL") == 0) {
			data->bitmask |= XT_IPVS_DIR;
			data->invert   &= ~XT_IPVS_DIR;
		} else if (strcasecmp(optarg, "REPLY") == 0) {
			data->bitmask |= XT_IPVS_DIR;
			data->invert  |= XT_IPVS_DIR;
		} else {
			xtables_param_act(XTF_BAD_VALUE,
					  "ipvs", "--vdir", optarg);
		}
		break;

	case '5': /* --vmethod */
		if (strcasecmp(optarg, "GATE") == 0)
			data->fwd_method = IP_VS_CONN_F_DROUTE;
		else if (strcasecmp(optarg, "IPIP") == 0)
			data->fwd_method = IP_VS_CONN_F_TUNNEL;
		else if (strcasecmp(optarg, "MASQ") == 0)
			data->fwd_method = IP_VS_CONN_F_MASQ;
		else
			xtables_param_act(XTF_BAD_VALUE,
					  "ipvs", "--vmethod", optarg);
		break;

	case '6': /* --vportctl */
		data->vportctl = htons(xtables_parse_port(optarg, "tcp"));
		break;

	default:
		/* Hu? How did we come here? */
		assert(false);
		return 0;
	}

	if (op & XT_IPVS_ONCE_MASK) {
		if (data->invert & XT_IPVS_IPVS_PROPERTY)
			xtables_error(PARAMETER_PROBLEM,
				      "! --ipvs cannot be together with"
				      " other options");
		data->bitmask |= XT_IPVS_IPVS_PROPERTY;
	}

	data->bitmask |= op;
	if (invert)
		data->invert |= op;
	*flags |= op;
	return 1;

multiple_use:
	xtables_error(PARAMETER_PROBLEM,
		      "multiple use of the same IPVS option is not allowed");
}
Example #3
0
static int policy_parse(int c, int invert, unsigned int *flags,
                        struct xt_policy_info *info, uint8_t family)
{
	struct xt_policy_elem *e = &info->pol[info->len];
	struct in_addr *addr = NULL, mask;
	struct in6_addr *addr6 = NULL, mask6;
	unsigned int naddr = 0, num;
	int mode;

	xtables_check_inverse(optarg, &invert, &optind, 0);

	switch (c) {
	case '1':
		if (info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT))
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: double --dir option");
		if (invert)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: can't invert --dir option");

		info->flags |= parse_direction(optarg);
		break;
	case '2':
		if (invert)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: can't invert --policy option");

		info->flags |= parse_policy(optarg);
		break;
	case '3':
		if (info->flags & XT_POLICY_MATCH_STRICT)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: double --strict option");

		if (invert)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: can't invert --strict option");

		info->flags |= XT_POLICY_MATCH_STRICT;
		break;
	case '4':
		if (e->match.reqid)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: double --reqid option");

		e->match.reqid = 1;
		e->invert.reqid = invert;
		if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX))
			xtables_param_act(XTF_BAD_VALUE, "policy", "--spi", optarg);
		e->reqid = num;
		break;
	case '5':
		if (e->match.spi)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: double --spi option");

		e->match.spi = 1;
		e->invert.spi = invert;
		if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX))
			xtables_param_act(XTF_BAD_VALUE, "policy", "--spi", optarg);
		e->spi = num;
		break;
	case '6':
		if (e->match.saddr)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: double --tunnel-src option");

		if (family == NFPROTO_IPV6)
			xtables_ip6parse_any(optarg, &addr6, &mask6, &naddr);
		else
			xtables_ipparse_any(optarg, &addr, &mask, &naddr);
		if (naddr > 1)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: name resolves to multiple IPs");

		e->match.saddr = 1;
		e->invert.saddr = invert;
		if (family == NFPROTO_IPV6) {
			memcpy(&e->saddr.a6, addr6, sizeof(*addr6));
			memcpy(&e->smask.a6, &mask6, sizeof(mask6));
		} else {
			e->saddr.a4 = addr[0];
			e->smask.a4 = mask;
		}
                break;
	case '7':
		if (e->match.daddr)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: double --tunnel-dst option");

		if (family == NFPROTO_IPV6)
			xtables_ip6parse_any(optarg, &addr6, &mask6, &naddr);
		else
			xtables_ipparse_any(optarg, &addr, &mask, &naddr);
		if (naddr > 1)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: name resolves to multiple IPs");

		e->match.daddr = 1;
		e->invert.daddr = invert;
		if (family == NFPROTO_IPV6) {
			memcpy(&e->daddr.a6, addr6, sizeof(*addr6));
			memcpy(&e->dmask.a6, &mask6, sizeof(mask6));
		} else {
			e->daddr.a4 = addr[0];
			e->dmask.a4 = mask;
		}
		break;
	case '8':
		if (e->match.proto)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: double --proto option");

		e->proto = xtables_parse_protocol(optarg);
		if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
		    e->proto != IPPROTO_COMP)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: protocol must ah/esp/ipcomp");
		e->match.proto = 1;
		e->invert.proto = invert;
		break;
	case '9':
		if (e->match.mode)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: double --mode option");

		mode = parse_mode(optarg);
		e->match.mode = 1;
		e->invert.mode = invert;
		e->mode = mode;
		break;
	case 'a':
		if (invert)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: can't invert --next option");

		if (++info->len == XT_POLICY_MAX_ELEM)
			xtables_error(PARAMETER_PROBLEM,
			           "policy match: maximum policy depth reached");
		break;
	default:
		return 0;
	}

	return 1;
}
Example #4
0
static int
conntrack_mt_parse(int c, bool invert, unsigned int *flags,
                   struct xt_conntrack_mtinfo2 *info)
{
	unsigned int port;
	char *p;

	switch (c) {
	case '1': /* --ctstate */
		conntrack_ps_states(info, optarg);
		info->match_flags |= XT_CONNTRACK_STATE;
		if (invert)
			info->invert_flags |= XT_CONNTRACK_STATE;
		break;

	case '2': /* --ctproto */
		/* Canonicalize into lower case */
		for (p = optarg; *p != '\0'; ++p)
			*p = tolower(*p);
		info->l4proto = xtables_parse_protocol(optarg);

		if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
			xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
			           "never match protocol");

		info->match_flags |= XT_CONNTRACK_PROTO;
		if (invert)
			info->invert_flags |= XT_CONNTRACK_PROTO;
		break;

	case '7': /* --ctstatus */
		conntrack_ps_statuses(info, optarg);
		info->match_flags |= XT_CONNTRACK_STATUS;
		if (invert)
			info->invert_flags |= XT_CONNTRACK_STATUS;
		break;

	case '8': /* --ctexpire */
		conntrack_ps_expires(info, optarg);
		info->match_flags |= XT_CONNTRACK_EXPIRES;
		if (invert)
			info->invert_flags |= XT_CONNTRACK_EXPIRES;
		break;

	case 'a': /* --ctorigsrcport */
		if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX))
			xtables_param_act(XTF_BAD_VALUE, "conntrack",
			          "--ctorigsrcport", optarg);
		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
		info->origsrc_port = htons(port);
		if (invert)
			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
		break;

	case 'b': /* --ctorigdstport */
		if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX))
			xtables_param_act(XTF_BAD_VALUE, "conntrack",
			          "--ctorigdstport", optarg);
		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
		info->origdst_port = htons(port);
		if (invert)
			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
		break;

	case 'c': /* --ctreplsrcport */
		if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX))
			xtables_param_act(XTF_BAD_VALUE, "conntrack",
			          "--ctreplsrcport", optarg);
		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
		info->replsrc_port = htons(port);
		if (invert)
			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
		break;

	case 'd': /* --ctrepldstport */
		if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX))
			xtables_param_act(XTF_BAD_VALUE, "conntrack",
			          "--ctrepldstport", optarg);
		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
		info->repldst_port = htons(port);
		if (invert)
			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
		break;

	case 'e': /* --ctdir */
		xtables_param_act(XTF_NO_INVERT, "conntrack", "--ctdir", invert);
		if (strcasecmp(optarg, "ORIGINAL") == 0) {
			info->match_flags  |= XT_CONNTRACK_DIRECTION;
			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
		} else if (strcasecmp(optarg, "REPLY") == 0) {
			info->match_flags  |= XT_CONNTRACK_DIRECTION;
			info->invert_flags |= XT_CONNTRACK_DIRECTION;
		} else {
			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", optarg);
		}
		break;

	default:
		return false;
	}

	*flags = info->match_flags;
	return true;
}
Example #5
0
static int conntrack_parse(int c, char **argv, int invert, unsigned int *flags,
                           const void *entry, struct xt_entry_match **match)
{
	struct xt_conntrack_info *sinfo = (void *)(*match)->data;
	char *protocol = NULL;
	unsigned int naddrs = 0;
	struct in_addr *addrs = NULL;


	switch (c) {
	case '1':
		xtables_check_inverse(optarg, &invert, &optind, 0);

		parse_states(argv[optind-1], sinfo);
		if (invert) {
			sinfo->invflags |= XT_CONNTRACK_STATE;
		}
		sinfo->flags |= XT_CONNTRACK_STATE;
		break;

	case '2':
		xtables_check_inverse(optarg, &invert, &optind, 0);

		if(invert)
			sinfo->invflags |= XT_CONNTRACK_PROTO;

		/* Canonicalize into lower case */
		for (protocol = argv[optind-1]; *protocol; protocol++)
			*protocol = tolower(*protocol);

		protocol = argv[optind-1];
		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum =
			xtables_parse_protocol(protocol);

		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
		    && (sinfo->invflags & XT_INV_PROTO))
			xtables_error(PARAMETER_PROBLEM,
				   "rule would never match protocol");

		sinfo->flags |= XT_CONNTRACK_PROTO;
		break;

	case '3':
		xtables_check_inverse(optarg, &invert, &optind, 0);

		if (invert)
			sinfo->invflags |= XT_CONNTRACK_ORIGSRC;

		xtables_ipparse_any(argv[optind-1], &addrs,
					&sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
					&naddrs);
		if(naddrs > 1)
			xtables_error(PARAMETER_PROBLEM,
				"multiple IP addresses not allowed");

		if(naddrs == 1) {
			sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
		}

		sinfo->flags |= XT_CONNTRACK_ORIGSRC;
		break;

	case '4':
		xtables_check_inverse(optarg, &invert, &optind, 0);

		if (invert)
			sinfo->invflags |= XT_CONNTRACK_ORIGDST;

		xtables_ipparse_any(argv[optind-1], &addrs,
					&sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
					&naddrs);
		if(naddrs > 1)
			xtables_error(PARAMETER_PROBLEM,
				"multiple IP addresses not allowed");

		if(naddrs == 1) {
			sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
		}

		sinfo->flags |= XT_CONNTRACK_ORIGDST;
		break;

	case '5':
		xtables_check_inverse(optarg, &invert, &optind, 0);

		if (invert)
			sinfo->invflags |= XT_CONNTRACK_REPLSRC;

		xtables_ipparse_any(argv[optind-1], &addrs,
					&sinfo->sipmsk[IP_CT_DIR_REPLY],
					&naddrs);
		if(naddrs > 1)
			xtables_error(PARAMETER_PROBLEM,
				"multiple IP addresses not allowed");

		if(naddrs == 1) {
			sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
		}

		sinfo->flags |= XT_CONNTRACK_REPLSRC;
		break;

	case '6':
		xtables_check_inverse(optarg, &invert, &optind, 0);

		if (invert)
			sinfo->invflags |= XT_CONNTRACK_REPLDST;

		xtables_ipparse_any(argv[optind-1], &addrs,
					&sinfo->dipmsk[IP_CT_DIR_REPLY],
					&naddrs);
		if(naddrs > 1)
			xtables_error(PARAMETER_PROBLEM,
				"multiple IP addresses not allowed");

		if(naddrs == 1) {
			sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
		}

		sinfo->flags |= XT_CONNTRACK_REPLDST;
		break;

	case '7':
		xtables_check_inverse(optarg, &invert, &optind, 0);

		parse_statuses(argv[optind-1], sinfo);
		if (invert) {
			sinfo->invflags |= XT_CONNTRACK_STATUS;
		}
		sinfo->flags |= XT_CONNTRACK_STATUS;
		break;

	case '8':
		xtables_check_inverse(optarg, &invert, &optind, 0);

		parse_expires(argv[optind-1], sinfo);
		if (invert) {
			sinfo->invflags |= XT_CONNTRACK_EXPIRES;
		}
		sinfo->flags |= XT_CONNTRACK_EXPIRES;
		break;

	default:
		return 0;
	}

	*flags = sinfo->flags;
	return 1;
}
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;
}