1/* Shared library add-on to iptables to add MARK target support. */
2#include <stdio.h>
3#include <string.h>
4#include <stdlib.h>
5#include <getopt.h>
6
7#include <iptables.h>
8#include <linux/netfilter_ipv4/ip_tables.h>
9/* For 64bit kernel / 32bit userspace */
10#include "../include/linux/netfilter_ipv4/ipt_MARK.h"
11
12/* Function which prints out usage message. */
13static void
14help(void)
15{
16	printf(
17"MARK target v%s options:\n"
18"  --set-mark value                   Set nfmark value\n"
19"  --and-mark value                   Binary AND the nfmark with value\n"
20"  --or-mark  value                   Binary OR  the nfmark with value\n"
21"\n",
22IPTABLES_VERSION);
23}
24
25static struct option opts[] = {
26	{ "set-mark", 1, 0, '1' },
27	{ "and-mark", 1, 0, '2' },
28	{ "or-mark", 1, 0, '3' },
29	{ 0 }
30};
31
32/* Initialize the target. */
33static void
34init(struct ipt_entry_target *t, unsigned int *nfcache)
35{
36}
37
38/* Function which parses command options; returns true if it
39   ate an option */
40static int
41parse_v0(int c, char **argv, int invert, unsigned int *flags,
42	 const struct ipt_entry *entry,
43	 struct ipt_entry_target **target)
44{
45	struct ipt_mark_target_info *markinfo
46		= (struct ipt_mark_target_info *)(*target)->data;
47
48	switch (c) {
49	case '1':
50#ifdef KERNEL_64_USERSPACE_32
51		if (string_to_number_ll(optarg, 0, 0,
52				     &markinfo->mark))
53#else
54		if (string_to_number_l(optarg, 0, 0,
55				     &markinfo->mark))
56#endif
57			exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
58		if (*flags)
59			exit_error(PARAMETER_PROBLEM,
60			           "MARK target: Can't specify --set-mark twice");
61		*flags = 1;
62		break;
63	case '2':
64		exit_error(PARAMETER_PROBLEM,
65			   "MARK target: kernel too old for --and-mark");
66	case '3':
67		exit_error(PARAMETER_PROBLEM,
68			   "MARK target: kernel too old for --or-mark");
69	default:
70		return 0;
71	}
72
73	return 1;
74}
75
76static void
77final_check(unsigned int flags)
78{
79	if (!flags)
80		exit_error(PARAMETER_PROBLEM,
81		           "MARK target: Parameter --set/and/or-mark"
82			   " is required");
83}
84
85/* Function which parses command options; returns true if it
86   ate an option */
87static int
88parse_v1(int c, char **argv, int invert, unsigned int *flags,
89	 const struct ipt_entry *entry,
90	 struct ipt_entry_target **target)
91{
92	struct ipt_mark_target_info_v1 *markinfo
93		= (struct ipt_mark_target_info_v1 *)(*target)->data;
94
95	switch (c) {
96	case '1':
97	        markinfo->mode = IPT_MARK_SET;
98		break;
99	case '2':
100	        markinfo->mode = IPT_MARK_AND;
101		break;
102	case '3':
103	        markinfo->mode = IPT_MARK_OR;
104		break;
105	default:
106		return 0;
107	}
108
109#ifdef KERNEL_64_USERSPACE_32
110	if (string_to_number_ll(optarg, 0, 0,  &markinfo->mark))
111#else
112	if (string_to_number_l(optarg, 0, 0, &markinfo->mark))
113#endif
114		exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
115
116	if (*flags)
117		exit_error(PARAMETER_PROBLEM,
118			   "MARK target: Can't specify --set-mark twice");
119
120	*flags = 1;
121	return 1;
122}
123
124#ifdef KERNEL_64_USERSPACE_32
125static void
126print_mark(unsigned long long mark)
127{
128	printf("0x%llx ", mark);
129}
130#else
131static void
132print_mark(unsigned long mark)
133{
134	printf("0x%lx ", mark);
135}
136#endif
137
138/* Prints out the targinfo. */
139static void
140print_v0(const struct ipt_ip *ip,
141	 const struct ipt_entry_target *target,
142	 int numeric)
143{
144	const struct ipt_mark_target_info *markinfo =
145		(const struct ipt_mark_target_info *)target->data;
146	printf("MARK set ");
147	print_mark(markinfo->mark);
148}
149
150/* Saves the union ipt_targinfo in parsable form to stdout. */
151static void
152save_v0(const struct ipt_ip *ip, const struct ipt_entry_target *target)
153{
154	const struct ipt_mark_target_info *markinfo =
155		(const struct ipt_mark_target_info *)target->data;
156
157	printf("--set-mark ");
158	print_mark(markinfo->mark);
159}
160
161/* Prints out the targinfo. */
162static void
163print_v1(const struct ipt_ip *ip,
164	 const struct ipt_entry_target *target,
165	 int numeric)
166{
167	const struct ipt_mark_target_info_v1 *markinfo =
168		(const struct ipt_mark_target_info_v1 *)target->data;
169
170	switch (markinfo->mode) {
171	case IPT_MARK_SET:
172		printf("MARK set ");
173		break;
174	case IPT_MARK_AND:
175		printf("MARK and ");
176		break;
177	case IPT_MARK_OR:
178		printf("MARK or ");
179		break;
180	}
181	print_mark(markinfo->mark);
182}
183
184/* Saves the union ipt_targinfo in parsable form to stdout. */
185static void
186save_v1(const struct ipt_ip *ip, const struct ipt_entry_target *target)
187{
188	const struct ipt_mark_target_info_v1 *markinfo =
189		(const struct ipt_mark_target_info_v1 *)target->data;
190
191	switch (markinfo->mode) {
192	case IPT_MARK_SET:
193		printf("--set-mark ");
194		break;
195	case IPT_MARK_AND:
196		printf("--and-mark ");
197		break;
198	case IPT_MARK_OR:
199		printf("--or-mark ");
200		break;
201	}
202	print_mark(markinfo->mark);
203}
204
205static
206struct iptables_target mark_v0 = {
207	.next		= NULL,
208	.name		= "MARK",
209	.version	= IPTABLES_VERSION,
210	.revision	= 0,
211	.size		= IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
212	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
213	.help		= &help,
214	.init		= &init,
215	.parse		= &parse_v0,
216	.final_check	= &final_check,
217	.print		= &print_v0,
218	.save		= &save_v0,
219	.extra_opts	= opts
220};
221
222static
223struct iptables_target mark_v1 = {
224	.next		= NULL,
225	.name		= "MARK",
226	.version	= IPTABLES_VERSION,
227	.revision	= 1,
228	.size		= IPT_ALIGN(sizeof(struct ipt_mark_target_info_v1)),
229	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_mark_target_info_v1)),
230	.help		= &help,
231	.init		= &init,
232	.parse		= &parse_v1,
233	.final_check	= &final_check,
234	.print		= &print_v1,
235	.save		= &save_v1,
236	.extra_opts	= opts
237};
238
239void _init(void)
240{
241	register_target(&mark_v0);
242	register_target(&mark_v1);
243}
244