1#include <stdio.h>
2#include <string.h>
3#include <xtables.h>
4#include <linux/netfilter/nf_conntrack_common.h>
5#include <linux/netfilter/xt_state.h>
6
7#ifndef XT_STATE_UNTRACKED
8#define XT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
9#endif
10
11enum {
12	O_STATE = 0,
13};
14
15static void
16state_help(void)
17{
18	printf(
19"state match options:\n"
20" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
21"				State(s) to match\n");
22}
23
24static const struct xt_option_entry state_opts[] = {
25	{.name = "state", .id = O_STATE, .type = XTTYPE_STRING,
26	 .flags = XTOPT_MAND | XTOPT_INVERT},
27	XTOPT_TABLEEND,
28};
29
30static int
31state_parse_state(const char *state, size_t len, struct xt_state_info *sinfo)
32{
33	if (strncasecmp(state, "INVALID", len) == 0)
34		sinfo->statemask |= XT_STATE_INVALID;
35	else if (strncasecmp(state, "NEW", len) == 0)
36		sinfo->statemask |= XT_STATE_BIT(IP_CT_NEW);
37	else if (strncasecmp(state, "ESTABLISHED", len) == 0)
38		sinfo->statemask |= XT_STATE_BIT(IP_CT_ESTABLISHED);
39	else if (strncasecmp(state, "RELATED", len) == 0)
40		sinfo->statemask |= XT_STATE_BIT(IP_CT_RELATED);
41	else if (strncasecmp(state, "UNTRACKED", len) == 0)
42		sinfo->statemask |= XT_STATE_UNTRACKED;
43	else
44		return 0;
45	return 1;
46}
47
48static void
49state_parse_states(const char *arg, struct xt_state_info *sinfo)
50{
51	const char *comma;
52
53	while ((comma = strchr(arg, ',')) != NULL) {
54		if (comma == arg || !state_parse_state(arg, comma-arg, sinfo))
55			xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
56		arg = comma+1;
57	}
58	if (!*arg)
59		xtables_error(PARAMETER_PROBLEM, "\"--state\" requires a list of "
60					      "states with no spaces, e.g. "
61					      "ESTABLISHED,RELATED");
62	if (strlen(arg) == 0 || !state_parse_state(arg, strlen(arg), sinfo))
63		xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
64}
65
66static void state_parse(struct xt_option_call *cb)
67{
68	struct xt_state_info *sinfo = cb->data;
69
70	xtables_option_parse(cb);
71	state_parse_states(cb->arg, sinfo);
72	if (cb->invert)
73		sinfo->statemask = ~sinfo->statemask;
74}
75
76static void state_print_state(unsigned int statemask)
77{
78	const char *sep = "";
79
80	if (statemask & XT_STATE_INVALID) {
81		printf("%sINVALID", sep);
82		sep = ",";
83	}
84	if (statemask & XT_STATE_BIT(IP_CT_NEW)) {
85		printf("%sNEW", sep);
86		sep = ",";
87	}
88	if (statemask & XT_STATE_BIT(IP_CT_RELATED)) {
89		printf("%sRELATED", sep);
90		sep = ",";
91	}
92	if (statemask & XT_STATE_BIT(IP_CT_ESTABLISHED)) {
93		printf("%sESTABLISHED", sep);
94		sep = ",";
95	}
96	if (statemask & XT_STATE_UNTRACKED) {
97		printf("%sUNTRACKED", sep);
98		sep = ",";
99	}
100}
101
102static void
103state_print(const void *ip,
104      const struct xt_entry_match *match,
105      int numeric)
106{
107	const struct xt_state_info *sinfo = (const void *)match->data;
108
109	printf(" state ");
110	state_print_state(sinfo->statemask);
111}
112
113static void state_save(const void *ip, const struct xt_entry_match *match)
114{
115	const struct xt_state_info *sinfo = (const void *)match->data;
116
117	printf(" --state ");
118	state_print_state(sinfo->statemask);
119}
120
121static struct xtables_match state_match = {
122	.family		= NFPROTO_UNSPEC,
123	.name		= "state",
124	.version	= XTABLES_VERSION,
125	.size		= XT_ALIGN(sizeof(struct xt_state_info)),
126	.userspacesize	= XT_ALIGN(sizeof(struct xt_state_info)),
127	.help		= state_help,
128	.print		= state_print,
129	.save		= state_save,
130	.x6_parse	= state_parse,
131	.x6_options	= state_opts,
132};
133
134void _init(void)
135{
136	xtables_register_match(&state_match);
137}
138