int wrap_tpcall(unsigned int c, char **argv, int inv,
		struct xtables_target *t, struct xt_entry_target *tt, void *fw)
{
    int rv = 1;
    int err;

    if ((err = setjmp(env)) == 0) {
        t->t = tt; // FIXME: See above.
        xtables_option_tpcall(c, argv, inv, t, fw);
    } else {
        errno = err;
    }

    return rv;
}
Example #2
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,
};
int command_default(struct iptables_command_state *cs,
		    struct xtables_globals *gl)
{
	struct xtables_rule_match *matchp;
	struct xtables_match *m;

	if (cs->target != NULL &&
	    (cs->target->parse != NULL || cs->target->x6_parse != NULL) &&
	    cs->c >= cs->target->option_offset &&
	    cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) {
		xtables_option_tpcall(cs->c, cs->argv, cs->invert,
				      cs->target, &cs->fw);
		return 0;
	}

	for (matchp = cs->matches; matchp; matchp = matchp->next) {
		m = matchp->match;

		if (matchp->completed ||
		    (m->x6_parse == NULL && m->parse == NULL))
			continue;
		if (cs->c < matchp->match->option_offset ||
		    cs->c >= matchp->match->option_offset + XT_OPTION_OFFSET_SCALE)
			continue;
		xtables_option_mpcall(cs->c, cs->argv, cs->invert, m, &cs->fw);
		return 0;
	}

	/* Try loading protocol */
	m = load_proto(cs);
	if (m != NULL) {
		size_t size;

		cs->proto_used = 1;

		size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;

		m->m = xtables_calloc(1, size);
		m->m->u.match_size = size;
		strcpy(m->m->u.user.name, m->name);
		m->m->u.user.revision = m->revision;
		xs_init_match(m);

		if (m->x6_options != NULL)
			gl->opts = xtables_options_xfrm(gl->orig_opts,
							gl->opts,
							m->x6_options,
							&m->option_offset);
		else
			gl->opts = xtables_merge_options(gl->orig_opts,
							 gl->opts,
							 m->extra_opts,
							 &m->option_offset);
		if (gl->opts == NULL)
			xtables_error(OTHER_PROBLEM, "can't alloc memory!");
		optind--;
		/* Indicate to rerun getopt *immediately* */
 		return 1;
	}

	if (cs->c == ':')
		xtables_error(PARAMETER_PROBLEM, "option \"%s\" "
		              "requires an argument", cs->argv[optind-1]);
	if (cs->c == '?')
		xtables_error(PARAMETER_PROBLEM, "unknown option "
			      "\"%s\"", cs->argv[optind-1]);
	xtables_error(PARAMETER_PROBLEM, "Unknown arg \"%s\"", optarg);
	return 0;
}
Example #4
0
int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
{
	struct arptables_command_state cs;
	int invert = 0;
	unsigned int nsaddrs = 0, ndaddrs = 0;
	struct in_addr *saddrs = NULL, *daddrs = NULL;

	int c, verbose = 0;
	const char *chain = NULL;
	const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
	const char *policy = NULL, *newname = NULL;
	unsigned int rulenum = 0, options = 0, command = 0;
	const char *pcnt = NULL, *bcnt = NULL;
	int ret = 1;
	struct xtables_target *t;

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

	opts = original_opts;
	global_option_offset = 0;

	xtables_globals.orig_opts = original_opts;

	/* re-set optind to 0 in case do_command gets called
	 * a second time */
	optind = 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;

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

		case 'D':
			add_command(&command, CMD_DELETE, CMD_NONE,
				    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,
				    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,
				    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,
				    invert);
			if (optarg) chain = optarg;
			else if (optind < argc && argv[optind][0] != '-'
				 && argv[optind][0] != '!')
				chain = argv[optind++];
			break;

		case 'F':
			add_command(&command, CMD_FLUSH, CMD_NONE,
				    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,
				    invert);
			if (optarg) chain = optarg;
			else if (optind < argc && argv[optind][0] != '-'
				&& argv[optind][0] != '!')
				chain = argv[optind++];
			break;

		case 'N':
			if (optarg && *optarg == '-')
				xtables_error(PARAMETER_PROBLEM,
					      "chain name not allowed to start "
					      "with `-'\n");
			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,
				    invert);
			chain = optarg;
			break;

		case 'X':
			add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
				    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,
				    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,
				    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];

			exit_printhelp();
			break;
		case 's':
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_S_IP, &cs.fw.arp.invflags,
				   invert);
			shostnetworkmask = argv[optind-1];
			break;

		case 'd':
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_D_IP, &cs.fw.arp.invflags,
				   invert);
			dhostnetworkmask = argv[optind-1];
			break;

		case 2:/* src-mac */
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_S_MAC, &cs.fw.arp.invflags,
				   invert);
			if (getmac_and_mask(argv[optind - 1],
			    cs.fw.arp.src_devaddr.addr, cs.fw.arp.src_devaddr.mask))
				xtables_error(PARAMETER_PROBLEM, "Problem with specified "
						"source mac");
			break;

		case 3:/* dst-mac */
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_D_MAC, &cs.fw.arp.invflags,
				   invert);

			if (getmac_and_mask(argv[optind - 1],
			    cs.fw.arp.tgt_devaddr.addr, cs.fw.arp.tgt_devaddr.mask))
				xtables_error(PARAMETER_PROBLEM, "Problem with specified "
						"destination mac");
			break;

		case 'l':/* hardware length */
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_H_LENGTH, &cs.fw.arp.invflags,
				   invert);
			getlength_and_mask(argv[optind - 1], &cs.fw.arp.arhln,
					   &cs.fw.arp.arhln_mask);

			if (cs.fw.arp.arhln != 6) {
				xtables_error(PARAMETER_PROBLEM,
					      "Only harware address length of"
					      " 6 is supported currently.");
			}

			break;

		case 8:/* protocol length */
			xtables_error(PARAMETER_PROBLEM, "not supported");
/*
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_P_LENGTH, &cs.fw.arp.invflags,
				   invert);

			getlength_and_mask(argv[optind - 1], &cs.fw.arp.arpln,
					   &cs.fw.arp.arpln_mask);
			break;
*/

		case 4:/* opcode */
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_OPCODE, &cs.fw.arp.invflags,
				   invert);
			if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpop,
					   &cs.fw.arp.arpop_mask, 10)) {
				int i;

				for (i = 0; i < NUMOPCODES; i++)
					if (!strcasecmp(opcodes[i], optarg))
						break;
				if (i == NUMOPCODES)
					xtables_error(PARAMETER_PROBLEM, "Problem with specified opcode");
				cs.fw.arp.arpop = htons(i+1);
			}
			break;

		case 5:/* h-type */
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_H_TYPE, &cs.fw.arp.invflags,
				   invert);
			if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arhrd,
					   &cs.fw.arp.arhrd_mask, 16)) {
				if (strcasecmp(argv[optind-1], "Ethernet"))
					xtables_error(PARAMETER_PROBLEM, "Problem with specified hardware type");
				cs.fw.arp.arhrd = htons(1);
			}
			break;

		case 6:/* proto-type */
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_P_TYPE, &cs.fw.arp.invflags,
				   invert);
			if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpro,
					   &cs.fw.arp.arpro_mask, 0)) {
				if (strcasecmp(argv[optind-1], "ipv4"))
					xtables_error(PARAMETER_PROBLEM, "Problem with specified protocol type");
				cs.fw.arp.arpro = htons(0x800);
			}
			break;

		case 'j':
			set_option(&options, OPT_JUMP, &cs.fw.arp.invflags,
				   invert);
			cs.jumpto = parse_target(optarg);
			cs.target = command_jump(&cs.fw, cs.jumpto);
			break;

		case 'i':
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_VIANAMEIN, &cs.fw.arp.invflags,
				   invert);
			parse_interface(argv[optind-1],
					cs.fw.arp.iniface,
					cs.fw.arp.iniface_mask);
/*			cs.fw.nfcache |= NFC_IP_IF_IN; */
			break;

		case 'o':
			check_inverse(optarg, &invert, &optind, argc);
			set_option(&options, OPT_VIANAMEOUT, &cs.fw.arp.invflags,
				   invert);
			parse_interface(argv[optind-1],
					cs.fw.arp.outiface,
					cs.fw.arp.outiface_mask);
			/* cs.fw.nfcache |= NFC_IP_IF_OUT; */
			break;

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

		case 'm': /*{
			size_t size;

			if (invert)
				exit_error(PARAMETER_PROBLEM,
					   "unexpected ! flag before --match");

			m = find_match(optarg, LOAD_MUST_SUCCEED);
			size = ARPT_ALIGN(sizeof(struct arpt_entry_match))
					 + m->size;
			m->m = fw_calloc(1, size);
			m->m->u.match_size = size;
			strcpy(m->m->u.user.name, m->name);
			m->init(m->m, &fw.nfcache);
			opts = merge_options(opts, m->extra_opts, &m->option_offset);
		}*/
		break;

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

		case 't':
			if (invert)
				xtables_error(PARAMETER_PROBLEM,
					      "unexpected ! flag before --table");
			*table = argv[optind-1];
			break;

		case 'V':
			if (invert)
				printf("Not %s ;-)\n", program_version);
			else
				printf("%s v%s\n",
				       program_name, program_version);
			exit(0);

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

		case 'M':
			//modprobe = optarg;
			break;

		case 'c':

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

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

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

			break;


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

		default:
			if (cs.target) {
				xtables_option_tpcall(c, argv,
						      invert, cs.target, &cs.fw);
			}
			break;
		}
		invert = FALSE;
	}

	if (cs.target)
		xtables_option_tfcall(cs.target);

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

	if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
		if (!(options & OPT_D_IP))
			dhostnetworkmask = "0.0.0.0/0";
		if (!(options & OPT_S_IP))
			shostnetworkmask = "0.0.0.0/0";
	}

	if (shostnetworkmask)
		parse_hostnetworkmask(shostnetworkmask, &saddrs,
				      &(cs.fw.arp.smsk), &nsaddrs);

	if (dhostnetworkmask)
		parse_hostnetworkmask(dhostnetworkmask, &daddrs,
				      &(cs.fw.arp.tmsk), &ndaddrs);

	if ((nsaddrs > 1 || ndaddrs > 1) &&
	    (cs.fw.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
		xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple"
				" source or destination IP addresses");

	if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
						 "specify a unique address");

	generic_opt_check(command, options);

	if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN)
		xtables_error(PARAMETER_PROBLEM,
				"chain name `%s' too long (must be under %i chars)",
				chain, ARPT_FUNCTION_MAXNAMELEN);

	if (nft_init(h, xtables_arp) < 0)
		xtables_error(OTHER_PROBLEM,
			      "Could not initialize nftables layer.");

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

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

		if (!cs.target && strlen(cs.jumpto) != 0) {
			size_t size;

			cs.target = xtables_find_target(XT_STANDARD_TARGET,
							XTF_LOAD_MUST_SUCCEED);
			size = sizeof(struct arpt_entry_target) + cs.target->size;
			cs.target->t = xtables_calloc(1, size);
			cs.target->t->u.target_size = size;
			strcpy(cs.target->t->u.user.name, cs.jumpto);
		}
	}

	switch (command) {
	case CMD_APPEND:
		ret = append_entry(h, chain, *table, &cs, 0,
				   nsaddrs, saddrs, ndaddrs, daddrs,
				   options&OPT_VERBOSE, true);
		break;
	case CMD_DELETE:
		ret = delete_entry(chain, *table, &cs,
				   nsaddrs, saddrs, ndaddrs, daddrs,
				   options&OPT_VERBOSE, h);
		break;
	case CMD_DELETE_NUM:
		ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
		break;
	case CMD_REPLACE:
		ret = replace_entry(chain, *table, &cs, rulenum - 1,
				    saddrs, daddrs, options&OPT_VERBOSE, h);
		break;
	case CMD_INSERT:
		ret = append_entry(h, chain, *table, &cs, rulenum - 1,
				   nsaddrs, saddrs, ndaddrs, daddrs,
				   options&OPT_VERBOSE, false);
		break;
	case CMD_LIST:
		ret = list_entries(h, chain, *table,
				   rulenum,
				   options&OPT_VERBOSE,
				   options&OPT_NUMERIC,
				   /*options&OPT_EXPANDED*/0,
				   options&OPT_LINENUMBERS);
		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_LIST|CMD_ZERO:
		ret = list_entries(h, chain, *table, rulenum,
				   options&OPT_VERBOSE,
				   options&OPT_NUMERIC,
				   /*options&OPT_EXPANDED*/0,
				   options&OPT_LINENUMBERS);
		if (ret)
			ret = nft_chain_zero_counters(h, chain, *table);
		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);*/

	return ret;
}