#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include <linux/netfilter/nf_conntrack_common.h>
#include <linux/netfilter/xt_state.h>
#ifndef XT_STATE_UNTRACKED
#define XT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
#endif
enum {
O_STATE = 0,
};
static void
state_help(void)
{
printf(
"state match options:\n"
" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
" State(s) to match\n");
}
static const struct xt_option_entry state_opts[] = {
{.name = "state", .id = O_STATE, .type = XTTYPE_STRING,
.flags = XTOPT_MAND},
XTOPT_TABLEEND,
};
static int
state_parse_state(const char *state, size_t len, struct xt_state_info *sinfo)
{
if (strncasecmp(state, "INVALID", len) == 0)
sinfo->statemask |= XT_STATE_INVALID;
else if (strncasecmp(state, "NEW", len) == 0)
sinfo->statemask |= XT_STATE_BIT(IP_CT_NEW);
else if (strncasecmp(state, "ESTABLISHED", len) == 0)
sinfo->statemask |= XT_STATE_BIT(IP_CT_ESTABLISHED);
else if (strncasecmp(state, "RELATED", len) == 0)
sinfo->statemask |= XT_STATE_BIT(IP_CT_RELATED);
else if (strncasecmp(state, "UNTRACKED", len) == 0)
sinfo->statemask |= XT_STATE_UNTRACKED;
else
return 0;
return 1;
}
static void
state_parse_states(const char *arg, struct xt_state_info *sinfo)
{
const char *comma;
while ((comma = strchr(arg, ',')) != NULL) {
if (comma == arg || !state_parse_state(arg, comma-arg, sinfo))
xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
arg = comma+1;
}
if (!*arg)
xtables_error(PARAMETER_PROBLEM, "\"--state\" requires a list of "
"states with no spaces, e.g. "
"ESTABLISHED,RELATED");
if (strlen(arg) == 0 || !state_parse_state(arg, strlen(arg), sinfo))
xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
}
static void state_parse(struct xt_option_call *cb)
{
struct xt_state_info *sinfo = cb->data;
xtables_option_parse(cb);
state_parse_states(cb->arg, sinfo);
if (cb->invert)
sinfo->statemask = ~sinfo->statemask;
}
static void state_print_state(unsigned int statemask)
{
const char *sep = "";
if (statemask & XT_STATE_INVALID) {
printf("%sINVALID", sep);
sep = ",";
}
if (statemask & XT_STATE_BIT(IP_CT_NEW)) {
printf("%sNEW", sep);
sep = ",";
}
if (statemask & XT_STATE_BIT(IP_CT_RELATED)) {
printf("%sRELATED", sep);
sep = ",";
}
if (statemask & XT_STATE_BIT(IP_CT_ESTABLISHED)) {
printf("%sESTABLISHED", sep);
sep = ",";
}
if (statemask & XT_STATE_UNTRACKED) {
printf("%sUNTRACKED", sep);
sep = ",";
}
}
static void
state_print(const void *ip,
const struct xt_entry_match *match,
int numeric)
{
const struct xt_state_info *sinfo = (const void *)match->data;
printf(" state ");
state_print_state(sinfo->statemask);
}
static void state_save(const void *ip, const struct xt_entry_match *match)
{
const struct xt_state_info *sinfo = (const void *)match->data;
printf(" --state ");
state_print_state(sinfo->statemask);
}
static struct xtables_match state_match = {
.family = NFPROTO_UNSPEC,
.name = "state",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_state_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_state_info)),
.help = state_help,
.print = state_print,
.save = state_save,
.x6_parse = state_parse,
.x6_options = state_opts,
};
void _init(void)
{
xtables_register_match(&state_match);
}