1/* Shared library add-on to iptables to add CONNMARK target support.
2 *
3 * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
4 * by Henrik Nordstrom <hno@marasystems.com>
5 *
6 * Version 1.1
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22#include <stdbool.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <xtables.h>
26#include <linux/netfilter/xt_CONNMARK.h>
27
28struct xt_connmark_target_info {
29	unsigned long mark;
30	unsigned long mask;
31	uint8_t mode;
32};
33
34enum {
35	O_SET_MARK = 0,
36	O_SAVE_MARK,
37	O_SET_RETURN,
38	O_RESTORE_MARK,
39	O_AND_MARK,
40	O_OR_MARK,
41	O_XOR_MARK,
42	O_SET_XMARK,
43	O_CTMASK,
44	O_NFMASK,
45	O_MASK,
46	F_SET_MARK     = 1 << O_SET_MARK,
47	F_SAVE_MARK    = 1 << O_SAVE_MARK,
48	F_SET_RETURN   = 1 << O_SET_RETURN,
49	F_RESTORE_MARK = 1 << O_RESTORE_MARK,
50	F_AND_MARK     = 1 << O_AND_MARK,
51	F_OR_MARK      = 1 << O_OR_MARK,
52	F_XOR_MARK     = 1 << O_XOR_MARK,
53	F_SET_XMARK    = 1 << O_SET_XMARK,
54	F_CTMASK       = 1 << O_CTMASK,
55	F_NFMASK       = 1 << O_NFMASK,
56	F_MASK         = 1 << O_MASK,
57	F_OP_ANY       = F_SET_MARK | F_SAVE_MARK | F_SET_RETURN | F_RESTORE_MARK |
58	                 F_AND_MARK | F_OR_MARK | F_XOR_MARK | F_SET_XMARK,
59};
60
61static void CONNMARK_help(void)
62{
63	printf(
64"CONNMARK target options:\n"
65"  --set-mark value[/mask]       Set conntrack mark value\n"
66"  --set-return [--mask mask]    Set conntrack mark & nfmark, RETURN\n"
67"  --save-mark [--mask mask]     Save the packet nfmark in the connection\n"
68"  --restore-mark [--mask mask]  Restore saved nfmark value\n");
69}
70
71#define s struct xt_connmark_target_info
72static const struct xt_option_entry CONNMARK_opts[] = {
73	{.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32,
74	 .excl = F_OP_ANY},
75	{.name = "save-mark", .id = O_SAVE_MARK, .type = XTTYPE_NONE,
76	 .excl = F_OP_ANY},
77	{.name = "set-return", .id = O_SET_RETURN, .type = XTTYPE_MARKMASK32,
78	 .excl = F_OP_ANY},
79	{.name = "restore-mark", .id = O_RESTORE_MARK, .type = XTTYPE_NONE,
80	 .excl = F_OP_ANY},
81	{.name = "mask", .id = O_MASK, .type = XTTYPE_UINT32},
82	XTOPT_TABLEEND,
83};
84#undef s
85
86#define s struct xt_connmark_tginfo1
87static const struct xt_option_entry connmark_tg_opts[] = {
88	{.name = "set-xmark", .id = O_SET_XMARK, .type = XTTYPE_MARKMASK32,
89	 .excl = F_OP_ANY},
90	{.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32,
91	 .excl = F_OP_ANY},
92	{.name = "set-return", .id = O_SET_RETURN, .type = XTTYPE_MARKMASK32,
93	 .excl = F_OP_ANY},
94	{.name = "and-mark", .id = O_AND_MARK, .type = XTTYPE_UINT32,
95	 .excl = F_OP_ANY},
96	{.name = "or-mark", .id = O_OR_MARK, .type = XTTYPE_UINT32,
97	 .excl = F_OP_ANY},
98	{.name = "xor-mark", .id = O_XOR_MARK, .type = XTTYPE_UINT32,
99	 .excl = F_OP_ANY},
100	{.name = "save-mark", .id = O_SAVE_MARK, .type = XTTYPE_NONE,
101	 .excl = F_OP_ANY},
102	{.name = "restore-mark", .id = O_RESTORE_MARK, .type = XTTYPE_NONE,
103	 .excl = F_OP_ANY},
104	{.name = "ctmask", .id = O_CTMASK, .type = XTTYPE_UINT32,
105	 .excl = F_MASK, .flags = XTOPT_PUT, XTOPT_POINTER(s, ctmask)},
106	{.name = "nfmask", .id = O_NFMASK, .type = XTTYPE_UINT32,
107	 .excl = F_MASK, .flags = XTOPT_PUT, XTOPT_POINTER(s, nfmask)},
108	{.name = "mask", .id = O_MASK, .type = XTTYPE_UINT32,
109	 .excl = F_CTMASK | F_NFMASK},
110	XTOPT_TABLEEND,
111};
112#undef s
113
114static void connmark_tg_help(void)
115{
116	printf(
117"CONNMARK target options:\n"
118"  --set-xmark value[/ctmask]    Zero mask bits and XOR ctmark with value\n"
119"  --save-mark [--ctmask mask] [--nfmask mask]\n"
120"                                Copy ctmark to nfmark using masks\n"
121"  --restore-mark [--ctmask mask] [--nfmask mask]\n"
122"                                Copy nfmark to ctmark using masks\n"
123"  --set-mark value[/mask]       Set conntrack mark value\n"
124"  --set-return value[/mask]     Set conntrack mark & nfmark, RETURN\n"
125"  --save-mark [--mask mask]     Save the packet nfmark in the connection\n"
126"  --restore-mark [--mask mask]  Restore saved nfmark value\n"
127"  --and-mark value              Binary AND the ctmark with bits\n"
128"  --or-mark value               Binary OR  the ctmark with bits\n"
129"  --xor-mark value              Binary XOR the ctmark with bits\n"
130);
131}
132
133static void connmark_tg_init(struct xt_entry_target *target)
134{
135	struct xt_connmark_tginfo1 *info = (void *)target->data;
136
137	/*
138	 * Need these defaults for --save-mark/--restore-mark if no
139	 * --ctmark or --nfmask is given.
140	 */
141	info->ctmask = UINT32_MAX;
142	info->nfmask = UINT32_MAX;
143}
144
145static void CONNMARK_parse(struct xt_option_call *cb)
146{
147	struct xt_connmark_target_info *markinfo = cb->data;
148
149	xtables_option_parse(cb);
150	switch (cb->entry->id) {
151	case O_SET_MARK:
152		markinfo->mode = XT_CONNMARK_SET;
153		markinfo->mark = cb->val.mark;
154		markinfo->mask = cb->val.mask;
155		break;
156	case O_SET_RETURN:
157		markinfo->mode = XT_CONNMARK_SET_RETURN;
158		markinfo->mark = cb->val.mark;
159		markinfo->mask = cb->val.mask;
160		break;
161	case O_SAVE_MARK:
162		markinfo->mode = XT_CONNMARK_SAVE;
163		break;
164	case O_RESTORE_MARK:
165		markinfo->mode = XT_CONNMARK_RESTORE;
166		break;
167	case O_MASK:
168		markinfo->mask = cb->val.u32;
169		break;
170	}
171}
172
173static void connmark_tg_parse(struct xt_option_call *cb)
174{
175	struct xt_connmark_tginfo1 *info = cb->data;
176
177	xtables_option_parse(cb);
178	switch (cb->entry->id) {
179	case O_SET_XMARK:
180		info->mode   = XT_CONNMARK_SET;
181		info->ctmark = cb->val.mark;
182		info->ctmask = cb->val.mask;
183		break;
184	case O_SET_MARK:
185		info->mode   = XT_CONNMARK_SET;
186		info->ctmark = cb->val.mark;
187		info->ctmask = cb->val.mark | cb->val.mask;
188		break;
189	case O_SET_RETURN:
190		info->mode   = XT_CONNMARK_SET_RETURN;
191		info->ctmark = cb->val.mark;
192		info->ctmask = cb->val.mask;
193		break;
194	case O_AND_MARK:
195		info->mode   = XT_CONNMARK_SET;
196		info->ctmark = 0;
197		info->ctmask = ~cb->val.u32;
198		break;
199	case O_OR_MARK:
200		info->mode   = XT_CONNMARK_SET;
201		info->ctmark = cb->val.u32;
202		info->ctmask = cb->val.u32;
203		break;
204	case O_XOR_MARK:
205		info->mode   = XT_CONNMARK_SET;
206		info->ctmark = cb->val.u32;
207		info->ctmask = 0;
208		break;
209	case O_SAVE_MARK:
210		info->mode = XT_CONNMARK_SAVE;
211		break;
212	case O_RESTORE_MARK:
213		info->mode = XT_CONNMARK_RESTORE;
214		break;
215	case O_MASK:
216		info->nfmask = info->ctmask = cb->val.u32;
217		break;
218	}
219}
220
221static void connmark_tg_check(struct xt_fcheck_call *cb)
222{
223	if (!(cb->xflags & F_OP_ANY))
224		xtables_error(PARAMETER_PROBLEM,
225		           "CONNMARK target: No operation specified");
226}
227
228static void
229print_mark(unsigned long mark)
230{
231	printf("0x%lx", mark);
232}
233
234static void
235print_mask(const char *text, unsigned long mask)
236{
237	if (mask != 0xffffffffUL)
238		printf("%s0x%lx", text, mask);
239}
240
241static void CONNMARK_print(const void *ip,
242                           const struct xt_entry_target *target, int numeric)
243{
244	const struct xt_connmark_target_info *markinfo =
245		(const struct xt_connmark_target_info *)target->data;
246	switch (markinfo->mode) {
247	case XT_CONNMARK_SET:
248	    printf(" CONNMARK set ");
249	    print_mark(markinfo->mark);
250	    print_mask("/", markinfo->mask);
251	    break;
252	case XT_CONNMARK_SET_RETURN:
253	    printf(" CONNMARK set-return ");
254	    print_mark(markinfo->mark);
255	    print_mask("/", markinfo->mask);
256	    break;
257	case XT_CONNMARK_SAVE:
258	    printf(" CONNMARK save ");
259	    print_mask("mask ", markinfo->mask);
260	    break;
261	case XT_CONNMARK_RESTORE:
262	    printf(" CONNMARK restore ");
263	    print_mask("mask ", markinfo->mask);
264	    break;
265	default:
266	    printf(" ERROR: UNKNOWN CONNMARK MODE");
267	    break;
268	}
269}
270
271static void
272connmark_tg_print(const void *ip, const struct xt_entry_target *target,
273                  int numeric)
274{
275	const struct xt_connmark_tginfo1 *info = (const void *)target->data;
276
277	switch (info->mode) {
278	case XT_CONNMARK_SET:
279		if (info->ctmark == 0)
280			printf(" CONNMARK and 0x%x",
281			       (unsigned int)(uint32_t)~info->ctmask);
282		else if (info->ctmark == info->ctmask)
283			printf(" CONNMARK or 0x%x", info->ctmark);
284		else if (info->ctmask == 0)
285			printf(" CONNMARK xor 0x%x", info->ctmark);
286		else if (info->ctmask == 0xFFFFFFFFU)
287			printf(" CONNMARK set 0x%x", info->ctmark);
288		else
289			printf(" CONNMARK xset 0x%x/0x%x",
290			       info->ctmark, info->ctmask);
291		break;
292	case XT_CONNMARK_SET_RETURN:
293		if (info->ctmask == 0xFFFFFFFFU)
294			printf(" CONNMARK set-return 0x%x", info->ctmark);
295		else
296			printf(" CONNMARK set-return 0x%x/0x%x",
297			       info->ctmark, info->ctmask);
298		break;
299	case XT_CONNMARK_SAVE:
300		if (info->nfmask == UINT32_MAX && info->ctmask == UINT32_MAX)
301			printf(" CONNMARK save");
302		else if (info->nfmask == info->ctmask)
303			printf(" CONNMARK save mask 0x%x", info->nfmask);
304		else
305			printf(" CONNMARK save nfmask 0x%x ctmask ~0x%x",
306			       info->nfmask, info->ctmask);
307		break;
308	case XT_CONNMARK_RESTORE:
309		if (info->ctmask == UINT32_MAX && info->nfmask == UINT32_MAX)
310			printf(" CONNMARK restore");
311		else if (info->ctmask == info->nfmask)
312			printf(" CONNMARK restore mask 0x%x", info->ctmask);
313		else
314			printf(" CONNMARK restore ctmask 0x%x nfmask ~0x%x",
315			       info->ctmask, info->nfmask);
316		break;
317
318	default:
319		printf(" ERROR: UNKNOWN CONNMARK MODE");
320		break;
321	}
322}
323
324static void CONNMARK_save(const void *ip, const struct xt_entry_target *target)
325{
326	const struct xt_connmark_target_info *markinfo =
327		(const struct xt_connmark_target_info *)target->data;
328
329	switch (markinfo->mode) {
330	case XT_CONNMARK_SET:
331	    printf(" --set-mark ");
332	    print_mark(markinfo->mark);
333	    print_mask("/", markinfo->mask);
334	    break;
335	case XT_CONNMARK_SET_RETURN:
336	    printf(" --set-return ");
337	    print_mark(markinfo->mark);
338	    print_mask("/", markinfo->mask);
339	    break;
340	case XT_CONNMARK_SAVE:
341	    printf(" --save-mark ");
342	    print_mask("--mask ", markinfo->mask);
343	    break;
344	case XT_CONNMARK_RESTORE:
345	    printf(" --restore-mark ");
346	    print_mask("--mask ", markinfo->mask);
347	    break;
348	default:
349	    printf(" ERROR: UNKNOWN CONNMARK MODE");
350	    break;
351	}
352}
353
354static void CONNMARK_init(struct xt_entry_target *t)
355{
356	struct xt_connmark_target_info *markinfo
357		= (struct xt_connmark_target_info *)t->data;
358
359	markinfo->mask = 0xffffffffUL;
360}
361
362static void
363connmark_tg_save(const void *ip, const struct xt_entry_target *target)
364{
365	const struct xt_connmark_tginfo1 *info = (const void *)target->data;
366
367	switch (info->mode) {
368	case XT_CONNMARK_SET:
369		printf(" --set-xmark 0x%x/0x%x", info->ctmark, info->ctmask);
370		break;
371	case XT_CONNMARK_SET_RETURN:
372		printf(" --set-return 0x%x/0x%x", info->ctmark, info->ctmask);
373		break;
374	case XT_CONNMARK_SAVE:
375		printf(" --save-mark --nfmask 0x%x --ctmask 0x%x",
376		       info->nfmask, info->ctmask);
377		break;
378	case XT_CONNMARK_RESTORE:
379		printf(" --restore-mark --nfmask 0x%x --ctmask 0x%x",
380		       info->nfmask, info->ctmask);
381		break;
382	default:
383		printf(" ERROR: UNKNOWN CONNMARK MODE");
384		break;
385	}
386}
387
388static struct xtables_target connmark_tg_reg[] = {
389	{
390		.family        = NFPROTO_UNSPEC,
391		.name          = "CONNMARK",
392		.revision      = 0,
393		.version       = XTABLES_VERSION,
394		.size          = XT_ALIGN(sizeof(struct xt_connmark_target_info)),
395		.userspacesize = XT_ALIGN(sizeof(struct xt_connmark_target_info)),
396		.help          = CONNMARK_help,
397		.init          = CONNMARK_init,
398		.print         = CONNMARK_print,
399		.save          = CONNMARK_save,
400		.x6_parse      = CONNMARK_parse,
401		.x6_fcheck     = connmark_tg_check,
402		.x6_options    = CONNMARK_opts,
403	},
404	{
405		.version       = XTABLES_VERSION,
406		.name          = "CONNMARK",
407		.revision      = 1,
408		.family        = NFPROTO_UNSPEC,
409		.size          = XT_ALIGN(sizeof(struct xt_connmark_tginfo1)),
410		.userspacesize = XT_ALIGN(sizeof(struct xt_connmark_tginfo1)),
411		.help          = connmark_tg_help,
412		.init          = connmark_tg_init,
413		.print         = connmark_tg_print,
414		.save          = connmark_tg_save,
415		.x6_parse      = connmark_tg_parse,
416		.x6_fcheck     = connmark_tg_check,
417		.x6_options    = connmark_tg_opts,
418	},
419};
420
421void _init(void)
422{
423	xtables_register_targets(connmark_tg_reg, ARRAY_SIZE(connmark_tg_reg));
424}
425