1/*
2 * Copyright (c) 2010-2013 Patrick McHardy <kaber@trash.net>
3 */
4
5#include <stdio.h>
6#include <string.h>
7#include <xtables.h>
8#include <linux/netfilter/nf_conntrack_common.h>
9#include <linux/netfilter/xt_CT.h>
10
11static void ct_help(void)
12{
13	printf(
14"CT target options:\n"
15" --notrack			Don't track connection\n"
16" --helper name			Use conntrack helper 'name' for connection\n"
17" --ctevents event[,event...]	Generate specified conntrack events for connection\n"
18" --expevents event[,event...]	Generate specified expectation events for connection\n"
19" --zone ID			Assign/Lookup connection in zone ID\n"
20	);
21}
22
23static void ct_help_v1(void)
24{
25	printf(
26"CT target options:\n"
27" --notrack			Don't track connection\n"
28" --helper name			Use conntrack helper 'name' for connection\n"
29" --timeout name 		Use timeout policy 'name' for connection\n"
30" --ctevents event[,event...]	Generate specified conntrack events for connection\n"
31" --expevents event[,event...]	Generate specified expectation events for connection\n"
32" --zone ID			Assign/Lookup connection in zone ID\n"
33	);
34}
35
36enum {
37	O_NOTRACK = 0,
38	O_HELPER,
39	O_TIMEOUT,
40	O_CTEVENTS,
41	O_EXPEVENTS,
42	O_ZONE,
43};
44
45#define s struct xt_ct_target_info
46static const struct xt_option_entry ct_opts[] = {
47	{.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE},
48	{.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING,
49	 .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)},
50	{.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING},
51	{.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING},
52	{.name = "zone", .id = O_ZONE, .type = XTTYPE_UINT16,
53	 .flags = XTOPT_PUT, XTOPT_POINTER(s, zone)},
54	XTOPT_TABLEEND,
55};
56#undef s
57
58#define s struct xt_ct_target_info_v1
59static const struct xt_option_entry ct_opts_v1[] = {
60	{.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE},
61	{.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING,
62	 .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)},
63	{.name = "timeout", .id = O_TIMEOUT, .type = XTTYPE_STRING,
64	 .flags = XTOPT_PUT, XTOPT_POINTER(s, timeout)},
65	{.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING},
66	{.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING},
67	{.name = "zone", .id = O_ZONE, .type = XTTYPE_UINT16,
68	 .flags = XTOPT_PUT, XTOPT_POINTER(s, zone)},
69	XTOPT_TABLEEND,
70};
71#undef s
72
73struct event_tbl {
74	const char	*name;
75	unsigned int	event;
76};
77
78static const struct event_tbl ct_event_tbl[] = {
79	{ "new",		IPCT_NEW },
80	{ "related",		IPCT_RELATED },
81	{ "destroy",		IPCT_DESTROY },
82	{ "reply",		IPCT_REPLY },
83	{ "assured",		IPCT_ASSURED },
84	{ "protoinfo",		IPCT_PROTOINFO },
85	{ "helper",		IPCT_HELPER },
86	{ "mark",		IPCT_MARK },
87	{ "natseqinfo",		IPCT_NATSEQADJ },
88	{ "secmark",		IPCT_SECMARK },
89};
90
91static const struct event_tbl exp_event_tbl[] = {
92	{ "new",		IPEXP_NEW },
93};
94
95static uint32_t ct_parse_events(const struct event_tbl *tbl, unsigned int size,
96				const char *events)
97{
98	char str[strlen(events) + 1], *e = str, *t;
99	unsigned int mask = 0, i;
100
101	strcpy(str, events);
102	while ((t = strsep(&e, ","))) {
103		for (i = 0; i < size; i++) {
104			if (strcmp(t, tbl[i].name))
105				continue;
106			mask |= 1 << tbl[i].event;
107			break;
108		}
109
110		if (i == size)
111			xtables_error(PARAMETER_PROBLEM, "Unknown event type \"%s\"", t);
112	}
113
114	return mask;
115}
116
117static void ct_print_events(const char *pfx, const struct event_tbl *tbl,
118			    unsigned int size, uint32_t mask)
119{
120	const char *sep = "";
121	unsigned int i;
122
123	printf(" %s ", pfx);
124	for (i = 0; i < size; i++) {
125		if (mask & (1 << tbl[i].event)) {
126			printf("%s%s", sep, tbl[i].name);
127			sep = ",";
128		}
129	}
130}
131
132static void ct_parse(struct xt_option_call *cb)
133{
134	struct xt_ct_target_info *info = cb->data;
135
136	xtables_option_parse(cb);
137	switch (cb->entry->id) {
138	case O_NOTRACK:
139		info->flags |= XT_CT_NOTRACK;
140		break;
141	case O_CTEVENTS:
142		info->ct_events = ct_parse_events(ct_event_tbl, ARRAY_SIZE(ct_event_tbl), cb->arg);
143		break;
144	case O_EXPEVENTS:
145		info->exp_events = ct_parse_events(exp_event_tbl, ARRAY_SIZE(exp_event_tbl), cb->arg);
146		break;
147	}
148}
149
150static void ct_parse_v1(struct xt_option_call *cb)
151{
152	struct xt_ct_target_info_v1 *info = cb->data;
153
154	xtables_option_parse(cb);
155	switch (cb->entry->id) {
156	case O_NOTRACK:
157		info->flags |= XT_CT_NOTRACK;
158		break;
159	case O_CTEVENTS:
160		info->ct_events = ct_parse_events(ct_event_tbl,
161						  ARRAY_SIZE(ct_event_tbl),
162						  cb->arg);
163		break;
164	case O_EXPEVENTS:
165		info->exp_events = ct_parse_events(exp_event_tbl,
166						   ARRAY_SIZE(exp_event_tbl),
167						   cb->arg);
168		break;
169	}
170}
171
172static void ct_print(const void *ip, const struct xt_entry_target *target, int numeric)
173{
174	const struct xt_ct_target_info *info =
175		(const struct xt_ct_target_info *)target->data;
176
177	printf(" CT");
178	if (info->flags & XT_CT_NOTRACK)
179		printf(" notrack");
180	if (info->helper[0])
181		printf(" helper %s", info->helper);
182	if (info->ct_events)
183		ct_print_events("ctevents", ct_event_tbl,
184				ARRAY_SIZE(ct_event_tbl), info->ct_events);
185	if (info->exp_events)
186		ct_print_events("expevents", exp_event_tbl,
187				ARRAY_SIZE(exp_event_tbl), info->exp_events);
188	if (info->zone)
189		printf("zone %u ", info->zone);
190}
191
192static void
193ct_print_v1(const void *ip, const struct xt_entry_target *target, int numeric)
194{
195	const struct xt_ct_target_info_v1 *info =
196		(const struct xt_ct_target_info_v1 *)target->data;
197
198	if (info->flags & XT_CT_NOTRACK_ALIAS) {
199		printf (" NOTRACK");
200		return;
201	}
202	printf(" CT");
203	if (info->flags & XT_CT_NOTRACK)
204		printf(" notrack");
205	if (info->helper[0])
206		printf(" helper %s", info->helper);
207	if (info->timeout[0])
208		printf(" timeout %s", info->timeout);
209	if (info->ct_events)
210		ct_print_events("ctevents", ct_event_tbl,
211				ARRAY_SIZE(ct_event_tbl), info->ct_events);
212	if (info->exp_events)
213		ct_print_events("expevents", exp_event_tbl,
214				ARRAY_SIZE(exp_event_tbl), info->exp_events);
215	if (info->zone)
216		printf("zone %u ", info->zone);
217}
218
219static void ct_save(const void *ip, const struct xt_entry_target *target)
220{
221	const struct xt_ct_target_info *info =
222		(const struct xt_ct_target_info *)target->data;
223
224	if (info->flags & XT_CT_NOTRACK_ALIAS)
225		return;
226	if (info->flags & XT_CT_NOTRACK)
227		printf(" --notrack");
228	if (info->helper[0])
229		printf(" --helper %s", info->helper);
230	if (info->ct_events)
231		ct_print_events("--ctevents", ct_event_tbl,
232				ARRAY_SIZE(ct_event_tbl), info->ct_events);
233	if (info->exp_events)
234		ct_print_events("--expevents", exp_event_tbl,
235				ARRAY_SIZE(exp_event_tbl), info->exp_events);
236	if (info->zone)
237		printf(" --zone %u", info->zone);
238}
239
240static void ct_save_v1(const void *ip, const struct xt_entry_target *target)
241{
242	const struct xt_ct_target_info_v1 *info =
243		(const struct xt_ct_target_info_v1 *)target->data;
244
245	if (info->flags & XT_CT_NOTRACK_ALIAS)
246		return;
247	if (info->flags & XT_CT_NOTRACK)
248		printf(" --notrack");
249	if (info->helper[0])
250		printf(" --helper %s", info->helper);
251	if (info->timeout[0])
252		printf(" --timeout %s", info->timeout);
253	if (info->ct_events)
254		ct_print_events("--ctevents", ct_event_tbl,
255				ARRAY_SIZE(ct_event_tbl), info->ct_events);
256	if (info->exp_events)
257		ct_print_events("--expevents", exp_event_tbl,
258				ARRAY_SIZE(exp_event_tbl), info->exp_events);
259	if (info->zone)
260		printf(" --zone %u", info->zone);
261}
262
263static const char *
264ct_print_name_alias(const struct xt_entry_target *target)
265{
266	struct xt_ct_target_info *info = (void *)target->data;
267
268	return info->flags & XT_CT_NOTRACK_ALIAS ? "NOTRACK" : "CT";
269}
270
271static void notrack_ct0_tg_init(struct xt_entry_target *target)
272{
273	struct xt_ct_target_info *info = (void *)target->data;
274
275	info->flags = XT_CT_NOTRACK;
276}
277
278static void notrack_ct1_tg_init(struct xt_entry_target *target)
279{
280	struct xt_ct_target_info_v1 *info = (void *)target->data;
281
282	info->flags = XT_CT_NOTRACK;
283}
284
285static void notrack_ct2_tg_init(struct xt_entry_target *target)
286{
287	struct xt_ct_target_info_v1 *info = (void *)target->data;
288
289	info->flags = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS;
290}
291
292static struct xtables_target ct_target_reg[] = {
293	{
294		.family		= NFPROTO_UNSPEC,
295		.name		= "CT",
296		.version	= XTABLES_VERSION,
297		.size		= XT_ALIGN(sizeof(struct xt_ct_target_info)),
298		.userspacesize	= offsetof(struct xt_ct_target_info, ct),
299		.help		= ct_help,
300		.print		= ct_print,
301		.save		= ct_save,
302		.x6_parse	= ct_parse,
303		.x6_options	= ct_opts,
304	},
305	{
306		.family		= NFPROTO_UNSPEC,
307		.name		= "CT",
308		.revision	= 1,
309		.version	= XTABLES_VERSION,
310		.size		= XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
311		.userspacesize	= offsetof(struct xt_ct_target_info_v1, ct),
312		.help		= ct_help_v1,
313		.print		= ct_print_v1,
314		.save		= ct_save_v1,
315		.x6_parse	= ct_parse_v1,
316		.x6_options	= ct_opts_v1,
317	},
318	{
319		.family		= NFPROTO_UNSPEC,
320		.name		= "CT",
321		.revision	= 2,
322		.version	= XTABLES_VERSION,
323		.size		= XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
324		.userspacesize	= offsetof(struct xt_ct_target_info_v1, ct),
325		.help		= ct_help_v1,
326		.print		= ct_print_v1,
327		.save		= ct_save_v1,
328		.alias		= ct_print_name_alias,
329		.x6_parse	= ct_parse_v1,
330		.x6_options	= ct_opts_v1,
331	},
332	{
333		.family        = NFPROTO_UNSPEC,
334		.name          = "NOTRACK",
335		.real_name     = "CT",
336		.revision      = 0,
337		.version       = XTABLES_VERSION,
338		.size          = XT_ALIGN(sizeof(struct xt_ct_target_info)),
339		.userspacesize = offsetof(struct xt_ct_target_info, ct),
340		.init          = notrack_ct0_tg_init,
341	},
342	{
343		.family        = NFPROTO_UNSPEC,
344		.name          = "NOTRACK",
345		.real_name     = "CT",
346		.revision      = 1,
347		.version       = XTABLES_VERSION,
348		.size          = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
349		.userspacesize = offsetof(struct xt_ct_target_info_v1, ct),
350		.init          = notrack_ct1_tg_init,
351	},
352	{
353		.family        = NFPROTO_UNSPEC,
354		.name          = "NOTRACK",
355		.real_name     = "CT",
356		.revision      = 2,
357		.ext_flags     = XTABLES_EXT_ALIAS,
358		.version       = XTABLES_VERSION,
359		.size          = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
360		.userspacesize = offsetof(struct xt_ct_target_info_v1, ct),
361		.init          = notrack_ct2_tg_init,
362	},
363	{
364		.family        = NFPROTO_UNSPEC,
365		.name          = "NOTRACK",
366		.revision      = 0,
367		.version       = XTABLES_VERSION,
368	},
369};
370
371void _init(void)
372{
373	xtables_register_targets(ct_target_reg, ARRAY_SIZE(ct_target_reg));
374}
375