blob: 71f689901e1d4f825fc87c2247426d25a1b6b04e [file] [log] [blame]
#include <config.h>
#include <ctype.h>
#include <getopt.h>
#include <errno.h>
#include <libgen.h>
#include <netdb.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <xtables.h>
#include <math.h>
#include "xshared.h"
/*
* Print out any special helps. A user might like to be able to add a --help
* to the commandline, and see expected results. So we call help for all
* specified matches and targets.
*/
void print_extension_helps(const struct xtables_target *t,
const struct xtables_rule_match *m)
{
for (; t != NULL; t = t->next) {
if (t->used) {
printf("\n");
if (t->help == NULL)
printf("%s does not take any options\n",
t->name);
else
t->help();
}
}
for (; m != NULL; m = m->next) {
printf("\n");
if (m->match->help == NULL)
printf("%s does not take any options\n",
m->match->name);
else
m->match->help();
}
}
const char *
proto_to_name(uint8_t proto, int nolookup)
{
unsigned int i;
if (proto && !nolookup) {
struct protoent *pent = getprotobynumber(proto);
if (pent)
return pent->p_name;
}
for (i = 0; xtables_chain_protos[i].name != NULL; ++i)
if (xtables_chain_protos[i].num == proto)
return xtables_chain_protos[i].name;
return NULL;
}
static struct xtables_match *
find_proto(const char *pname, enum xtables_tryload tryload,
int nolookup, struct xtables_rule_match **matches)
{
unsigned int proto;
if (xtables_strtoui(pname, NULL, &proto, 0, UINT8_MAX)) {
const char *protoname = proto_to_name(proto, nolookup);
if (protoname)
return xtables_find_match(protoname, tryload, matches);
} else
return xtables_find_match(pname, tryload, matches);
return NULL;
}
/*
* Some explanations (after four different bugs in 3 different releases): If
* we encounter a parameter, that has not been parsed yet, it's not an option
* of an explicitly loaded match or a target. However, we support implicit
* loading of the protocol match extension. '-p tcp' means 'l4 proto 6' and at
* the same time 'load tcp protocol match on demand if we specify --dport'.
*
* To make this work, we need to make sure:
* - the parameter has not been parsed by a match (m above)
* - a protocol has been specified
* - the protocol extension has not been loaded yet, or is loaded and unused
* [think of ip6tables-restore!]
* - the protocol extension can be successively loaded
*/
static bool should_load_proto(struct iptables_command_state *cs)
{
if (cs->protocol == NULL)
return false;
if (find_proto(cs->protocol, XTF_DONT_LOAD,
cs->options & OPT_NUMERIC, NULL) == NULL)
return true;
return !cs->proto_used;
}
struct xtables_match *load_proto(struct iptables_command_state *cs)
{
if (!should_load_proto(cs))
return NULL;
return find_proto(cs->protocol, XTF_TRY_LOAD,
cs->options & OPT_NUMERIC, &cs->matches);
}
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);
}
static mainfunc_t subcmd_get(const char *cmd, const struct subcommand *cb)
{
for (; cb->name != NULL; ++cb)
if (strcmp(cb->name, cmd) == 0)
return cb->main;
return NULL;
}
int subcmd_main(int argc, char **argv, const struct subcommand *cb)
{
const char *cmd = basename(*argv);
mainfunc_t f = subcmd_get(cmd, cb);
if (f == NULL && argc > 1) {
/*
* Unable to find a main method for our command name?
* Let's try again with the first argument!
*/
++argv;
--argc;
f = subcmd_get(*argv, cb);
}
/* now we should have a valid function pointer */
if (f != NULL)
return f(argc, argv);
fprintf(stderr, "ERROR: No valid subcommand given.\nValid subcommands:\n");
for (; cb->name != NULL; ++cb)
fprintf(stderr, " * %s\n", cb->name);
exit(EXIT_FAILURE);
}
void xs_init_target(struct xtables_target *target)
{
if (target->udata_size != 0) {
free(target->udata);
target->udata = calloc(1, target->udata_size);
if (target->udata == NULL)
xtables_error(RESOURCE_PROBLEM, "malloc");
}
if (target->init != NULL)
target->init(target->t);
}
void xs_init_match(struct xtables_match *match)
{
if (match->udata_size != 0) {
/*
* As soon as a subsequent instance of the same match
* is used, e.g. "-m time -m time", the first instance
* is no longer reachable anyway, so we can free udata.
* Same goes for target.
*/
free(match->udata);
match->udata = calloc(1, match->udata_size);
if (match->udata == NULL)
xtables_error(RESOURCE_PROBLEM, "malloc");
}
if (match->init != NULL)
match->init(match->m);
}
static int xtables_lock(int wait, struct timeval *wait_interval)
{
struct timeval time_left, wait_time;
const char *lock_file;
int fd, i = 0;
time_left.tv_sec = wait;
time_left.tv_usec = 0;
lock_file = getenv("XTABLES_LOCKFILE");
if (lock_file == NULL || lock_file[0] == '\0')
lock_file = XT_LOCK_NAME;
fd = open(lock_file, O_CREAT, 0600);
if (fd < 0) {
fprintf(stderr, "Fatal: can't open lock file %s: %s\n",
lock_file, strerror(errno));
return XT_LOCK_FAILED;
}
if (wait == -1) {
if (flock(fd, LOCK_EX) == 0)
return fd;
fprintf(stderr, "Can't lock %s: %s\n", lock_file,
strerror(errno));
return XT_LOCK_BUSY;
}
while (1) {
if (flock(fd, LOCK_EX | LOCK_NB) == 0)
return fd;
else if (timercmp(&time_left, wait_interval, <))
return XT_LOCK_BUSY;
if (++i % 10 == 0) {
fprintf(stderr, "Another app is currently holding the xtables lock; "
"still %lds %ldus time ahead to have a chance to grab the lock...\n",
time_left.tv_sec, time_left.tv_usec);
}
wait_time = *wait_interval;
select(0, NULL, NULL, NULL, &wait_time);
timersub(&time_left, wait_interval, &time_left);
}
}
void xtables_unlock(int lock)
{
if (lock >= 0)
close(lock);
}
int xtables_lock_or_exit(int wait, struct timeval *wait_interval)
{
int lock = xtables_lock(wait, wait_interval);
if (lock == XT_LOCK_FAILED) {
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
if (lock == XT_LOCK_BUSY) {
fprintf(stderr, "Another app is currently holding the xtables lock. ");
if (wait == 0)
fprintf(stderr, "Perhaps you want to use the -w option?\n");
else
fprintf(stderr, "Stopped waiting after %ds.\n", wait);
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
return lock;
}
int parse_wait_time(int argc, char *argv[])
{
int wait = -1;
if (optarg) {
if (sscanf(optarg, "%i", &wait) != 1)
xtables_error(PARAMETER_PROBLEM,
"wait seconds not numeric");
} else if (xs_has_arg(argc, argv))
if (sscanf(argv[optind++], "%i", &wait) != 1)
xtables_error(PARAMETER_PROBLEM,
"wait seconds not numeric");
return wait;
}
void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval)
{
const char *arg;
unsigned int usec;
int ret;
if (optarg)
arg = optarg;
else if (xs_has_arg(argc, argv))
arg = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM, "wait interval value required");
ret = sscanf(arg, "%u", &usec);
if (ret == 1) {
if (usec > 999999)
xtables_error(PARAMETER_PROBLEM,
"too long usec wait %u > 999999 usec",
usec);
wait_interval->tv_sec = 0;
wait_interval->tv_usec = usec;
return;
}
xtables_error(PARAMETER_PROBLEM, "wait interval not numeric");
}
int parse_counters(const char *string, struct xt_counters *ctr)
{
int ret;
if (!string)
return 0;
ret = sscanf(string, "[%llu:%llu]",
(unsigned long long *)&ctr->pcnt,
(unsigned long long *)&ctr->bcnt);
return ret == 2;
}
/* Tokenize counters argument of typical iptables-restore format rule.
*
* If *bufferp contains counters, update *pcntp and *bcntp to point at them,
* change bytes after counters in *bufferp to nul-bytes, update *bufferp to
* point to after the counters and return true.
* If *bufferp does not contain counters, return false.
* If syntax is wrong in *bufferp, call xtables_error() and hence exit().
* */
bool tokenize_rule_counters(char **bufferp, char **pcntp, char **bcntp, int line)
{
char *ptr, *buffer = *bufferp, *pcnt, *bcnt;
if (buffer[0] != '[')
return false;
/* we have counters in our input */
ptr = strchr(buffer, ']');
if (!ptr)
xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]\n", line);
pcnt = strtok(buffer+1, ":");
if (!pcnt)
xtables_error(PARAMETER_PROBLEM, "Bad line %u: need :\n", line);
bcnt = strtok(NULL, "]");
if (!bcnt)
xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]\n", line);
*pcntp = pcnt;
*bcntp = bcnt;
/* start command parsing after counter */
*bufferp = ptr + 1;
return true;
}
inline bool xs_has_arg(int argc, char *argv[])
{
return optind < argc &&
argv[optind][0] != '-' &&
argv[optind][0] != '!';
}
/* function adding one argument to store, updating argc
* returns if argument added, does not return otherwise */
void add_argv(struct argv_store *store, const char *what, int quoted)
{
DEBUGP("add_argv: %s\n", what);
if (store->argc + 1 >= MAX_ARGC)
xtables_error(PARAMETER_PROBLEM,
"Parser cannot handle more arguments\n");
if (!what)
xtables_error(PARAMETER_PROBLEM,
"Trying to store NULL argument\n");
store->argv[store->argc] = strdup(what);
store->argvattr[store->argc] = quoted;
store->argv[++store->argc] = NULL;
}
void free_argv(struct argv_store *store)
{
while (store->argc) {
store->argc--;
free(store->argv[store->argc]);
store->argvattr[store->argc] = 0;
}
}
/* Save parsed rule for comparison with next rule to perform action aggregation
* on duplicate conditions.
*/
void save_argv(struct argv_store *dst, struct argv_store *src)
{
int i;
free_argv(dst);
for (i = 0; i < src->argc; i++) {
dst->argvattr[i] = src->argvattr[i];
dst->argv[i] = src->argv[i];
src->argv[i] = NULL;
}
dst->argc = src->argc;
src->argc = 0;
}
struct xt_param_buf {
char buffer[1024];
int len;
};
static void add_param(struct xt_param_buf *param, const char *curchar)
{
param->buffer[param->len++] = *curchar;
if (param->len >= sizeof(param->buffer))
xtables_error(PARAMETER_PROBLEM,
"Parameter too long!");
}
void add_param_to_argv(struct argv_store *store, char *parsestart, int line)
{
int quote_open = 0, escaped = 0, quoted = 0;
struct xt_param_buf param = {};
char *curchar;
/* After fighting with strtok enough, here's now
* a 'real' parser. According to Rusty I'm now no
* longer a real hacker, but I can live with that */
for (curchar = parsestart; *curchar; curchar++) {
if (quote_open) {
if (escaped) {
add_param(&param, curchar);
escaped = 0;
continue;
} else if (*curchar == '\\') {
escaped = 1;
continue;
} else if (*curchar == '"') {
quote_open = 0;
} else {
add_param(&param, curchar);
continue;
}
} else {
if (*curchar == '"') {
quote_open = 1;
quoted = 1;
continue;
}
}
switch (*curchar) {
case '"':
break;
case ' ':
case '\t':
case '\n':
if (!param.len) {
/* two spaces? */
continue;
}
break;
default:
/* regular character, copy to buffer */
add_param(&param, curchar);
continue;
}
param.buffer[param.len] = '\0';
add_argv(store, param.buffer, quoted);
param.len = 0;
quoted = 0;
}
if (param.len) {
param.buffer[param.len] = '\0';
add_argv(store, param.buffer, 0);
}
}
#ifdef DEBUG
void debug_print_argv(struct argv_store *store)
{
int i;
for (i = 0; i < store->argc; i++)
fprintf(stderr, "argv[%d]: %s\n", i, store->argv[i]);
}
#endif
static const char *ipv4_addr_to_string(const struct in_addr *addr,
const struct in_addr *mask,
unsigned int format)
{
static char buf[BUFSIZ];
if (!mask->s_addr && !(format & FMT_NUMERIC))
return "anywhere";
if (format & FMT_NUMERIC)
strncpy(buf, xtables_ipaddr_to_numeric(addr), BUFSIZ - 1);
else
strncpy(buf, xtables_ipaddr_to_anyname(addr), BUFSIZ - 1);
buf[BUFSIZ - 1] = '\0';
strncat(buf, xtables_ipmask_to_numeric(mask),
BUFSIZ - strlen(buf) - 1);
return buf;
}
void print_ipv4_addresses(const struct ipt_entry *fw, unsigned int format)
{
fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
printf(FMT("%-19s ", "%s "),
ipv4_addr_to_string(&fw->ip.src, &fw->ip.smsk, format));
fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
printf(FMT("%-19s ", "-> %s"),
ipv4_addr_to_string(&fw->ip.dst, &fw->ip.dmsk, format));
}
static const char *ipv6_addr_to_string(const struct in6_addr *addr,
const struct in6_addr *mask,
unsigned int format)
{
static char buf[BUFSIZ];
if (IN6_IS_ADDR_UNSPECIFIED(addr) && !(format & FMT_NUMERIC))
return "anywhere";
if (format & FMT_NUMERIC)
strncpy(buf, xtables_ip6addr_to_numeric(addr), BUFSIZ - 1);
else
strncpy(buf, xtables_ip6addr_to_anyname(addr), BUFSIZ - 1);
buf[BUFSIZ - 1] = '\0';
strncat(buf, xtables_ip6mask_to_numeric(mask),
BUFSIZ - strlen(buf) - 1);
return buf;
}
void print_ipv6_addresses(const struct ip6t_entry *fw6, unsigned int format)
{
fputc(fw6->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout);
printf(FMT("%-19s ", "%s "),
ipv6_addr_to_string(&fw6->ipv6.src,
&fw6->ipv6.smsk, format));
fputc(fw6->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout);
printf(FMT("%-19s ", "-> %s"),
ipv6_addr_to_string(&fw6->ipv6.dst,
&fw6->ipv6.dmsk, format));
}
/* Luckily, IPT_INV_VIA_IN and IPT_INV_VIA_OUT
* have the same values as IP6T_INV_VIA_IN and IP6T_INV_VIA_OUT
* so this function serves for both iptables and ip6tables */
void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
unsigned int format)
{
const char *anyname = format & FMT_NUMERIC ? "*" : "any";
char iface[IFNAMSIZ + 2];
if (!(format & FMT_VIA))
return;
snprintf(iface, IFNAMSIZ + 2, "%s%s",
invflags & IPT_INV_VIA_IN ? "!" : "",
iniface[0] != '\0' ? iniface : anyname);
printf(FMT(" %-6s ", "in %s "), iface);
snprintf(iface, IFNAMSIZ + 2, "%s%s",
invflags & IPT_INV_VIA_OUT ? "!" : "",
outiface[0] != '\0' ? outiface : anyname);
printf(FMT("%-6s ", "out %s "), iface);
}
void command_match(struct iptables_command_state *cs)
{
struct option *opts = xt_params->opts;
struct xtables_match *m;
size_t size;
if (cs->invert)
xtables_error(PARAMETER_PROBLEM,
"unexpected ! flag before --match");
m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
m->m = xtables_calloc(1, size);
m->m->u.match_size = size;
if (m->real_name == NULL) {
strcpy(m->m->u.user.name, m->name);
} else {
strcpy(m->m->u.user.name, m->real_name);
if (!(m->ext_flags & XTABLES_EXT_ALIAS))
fprintf(stderr, "Notice: the %s match is converted into %s match "
"in rule listing and saving.\n", m->name, m->real_name);
}
m->m->u.user.revision = m->revision;
xs_init_match(m);
if (m == m->next)
return;
/* Merge options for non-cloned matches */
if (m->x6_options != NULL)
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
m->x6_options, &m->option_offset);
else if (m->extra_opts != NULL)
opts = xtables_merge_options(xt_params->orig_opts, opts,
m->extra_opts, &m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xt_params->opts = opts;
}
const char *xt_parse_target(const char *targetname)
{
const char *ptr;
if (strlen(targetname) < 1)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name (too short)");
if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s' (%u chars max)",
targetname, XT_EXTENSION_MAXNAMELEN - 1);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s'", targetname);
return targetname;
}
void command_jump(struct iptables_command_state *cs, const char *jumpto)
{
struct option *opts = xt_params->opts;
size_t size;
cs->jumpto = xt_parse_target(jumpto);
/* TRY_LOAD (may be chain name) */
cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
if (cs->target == NULL)
return;
size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
cs->target->t = xtables_calloc(1, size);
cs->target->t->u.target_size = size;
if (cs->target->real_name == NULL) {
strcpy(cs->target->t->u.user.name, cs->jumpto);
} else {
/* Alias support for userspace side */
strcpy(cs->target->t->u.user.name, cs->target->real_name);
if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS))
fprintf(stderr, "Notice: The %s target is converted into %s target "
"in rule listing and saving.\n",
cs->jumpto, cs->target->real_name);
}
cs->target->t->u.user.revision = cs->target->revision;
xs_init_target(cs->target);
if (cs->target->x6_options != NULL)
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
cs->target->x6_options,
&cs->target->option_offset);
else
opts = xtables_merge_options(xt_params->orig_opts, opts,
cs->target->extra_opts,
&cs->target->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xt_params->opts = opts;
}
char cmd2char(int option)
{
/* cmdflags index corresponds with position of bit in CMD_* values */
static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
int i;
for (i = 0; option > 1; option >>= 1, i++)
;
if (i >= ARRAY_SIZE(cmdflags))
xtables_error(OTHER_PROBLEM,
"cmd2char(): Invalid command number %u.\n",
1 << i);
return cmdflags[i];
}
void add_command(unsigned int *cmd, const int newcmd,
const int othercmds, int invert)
{
if (invert)
xtables_error(PARAMETER_PROBLEM, "unexpected '!' flag");
if (*cmd & (~othercmds))
xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n",
cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
*cmd |= newcmd;
}
/* Can't be zero. */
int parse_rulenumber(const char *rule)
{
unsigned int rulenum;
if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX))
xtables_error(PARAMETER_PROBLEM,
"Invalid rule number `%s'", rule);
return rulenum;
}
/* Table of legal combinations of commands and options. If any of the
* given commands make an option legal, that option is legal (applies to
* CMD_LIST and CMD_ZERO only).
* Key:
* + compulsory
* x illegal
* optional
*/
static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
/* -n -s -d -p -j -v -x -i -o --line -c -f 2 3 l 4 5 6 */
/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' ',' ',' ',' ',' ',' ',' '},
/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
/*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' ','x','x','x','x','x','x','x','x'},
/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x','x','x','x','x','x','x'},
/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
/*CHECK*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' ',' ',' ',' ',' ',' ',' '},
};
void generic_opt_check(int command, int options)
{
int i, j, legal = 0;
/* Check that commands are valid with options. Complicated by the
* fact that if an option is legal with *any* command given, it is
* legal overall (ie. -z and -l).
*/
for (i = 0; i < NUMBER_OF_OPT; i++) {
legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
for (j = 0; j < NUMBER_OF_CMD; j++) {
if (!(command & (1<<j)))
continue;
if (!(options & (1<<i))) {
if (commands_v_options[j][i] == '+')
xtables_error(PARAMETER_PROBLEM,
"You need to supply the `-%c' "
"option for this command\n",
optflags[i]);
} else {
if (commands_v_options[j][i] != 'x')
legal = 1;
else if (legal == 0)
legal = -1;
}
}
if (legal == -1)
xtables_error(PARAMETER_PROBLEM,
"Illegal option `-%c' with this command\n",
optflags[i]);
}
}
char opt2char(int option)
{
const char *ptr;
for (ptr = optflags; option > 1; option >>= 1, ptr++)
;
return *ptr;
}