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
ct_help(void)11 static 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|mark} Assign/Lookup connection in zone ID/packet nfmark\n"
20 " --zone-orig {ID|mark} Same as 'zone' option, but only applies to ORIGINAL direction\n"
21 " --zone-reply {ID|mark} Same as 'zone' option, but only applies to REPLY direction\n"
22 );
23 }
24
ct_help_v1(void)25 static void ct_help_v1(void)
26 {
27 printf(
28 "CT target options:\n"
29 " --notrack Don't track connection\n"
30 " --helper name Use conntrack helper 'name' for connection\n"
31 " --timeout name Use timeout policy 'name' for connection\n"
32 " --ctevents event[,event...] Generate specified conntrack events for connection\n"
33 " --expevents event[,event...] Generate specified expectation events for connection\n"
34 " --zone {ID|mark} Assign/Lookup connection in zone ID/packet nfmark\n"
35 " --zone-orig {ID|mark} Same as 'zone' option, but only applies to ORIGINAL direction\n"
36 " --zone-reply {ID|mark} Same as 'zone' option, but only applies to REPLY direction\n"
37 );
38 }
39
40 enum {
41 O_NOTRACK = 0,
42 O_HELPER,
43 O_TIMEOUT,
44 O_CTEVENTS,
45 O_EXPEVENTS,
46 O_ZONE,
47 O_ZONE_ORIG,
48 O_ZONE_REPLY,
49 };
50
51 #define s struct xt_ct_target_info
52 static const struct xt_option_entry ct_opts[] = {
53 {.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE},
54 {.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING,
55 .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)},
56 {.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING},
57 {.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING},
58 {.name = "zone-orig", .id = O_ZONE_ORIG, .type = XTTYPE_STRING},
59 {.name = "zone-reply", .id = O_ZONE_REPLY, .type = XTTYPE_STRING},
60 {.name = "zone", .id = O_ZONE, .type = XTTYPE_STRING},
61 XTOPT_TABLEEND,
62 };
63 #undef s
64
65 #define s struct xt_ct_target_info_v1
66 static const struct xt_option_entry ct_opts_v1[] = {
67 {.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE},
68 {.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING,
69 .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)},
70 {.name = "timeout", .id = O_TIMEOUT, .type = XTTYPE_STRING,
71 .flags = XTOPT_PUT, XTOPT_POINTER(s, timeout)},
72 {.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING},
73 {.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING},
74 {.name = "zone-orig", .id = O_ZONE_ORIG, .type = XTTYPE_STRING},
75 {.name = "zone-reply", .id = O_ZONE_REPLY, .type = XTTYPE_STRING},
76 {.name = "zone", .id = O_ZONE, .type = XTTYPE_STRING},
77 XTOPT_TABLEEND,
78 };
79 #undef s
80
81 struct event_tbl {
82 const char *name;
83 unsigned int event;
84 };
85
86 static const struct event_tbl ct_event_tbl[] = {
87 { "new", IPCT_NEW },
88 { "related", IPCT_RELATED },
89 { "destroy", IPCT_DESTROY },
90 { "reply", IPCT_REPLY },
91 { "assured", IPCT_ASSURED },
92 { "protoinfo", IPCT_PROTOINFO },
93 { "helper", IPCT_HELPER },
94 { "mark", IPCT_MARK },
95 { "natseqinfo", IPCT_NATSEQADJ },
96 { "secmark", IPCT_SECMARK },
97 };
98
99 static const struct event_tbl exp_event_tbl[] = {
100 { "new", IPEXP_NEW },
101 };
102
ct_parse_zone_id(const char * opt,unsigned int opt_id,uint16_t * zone_id,uint16_t * flags)103 static void ct_parse_zone_id(const char *opt, unsigned int opt_id,
104 uint16_t *zone_id, uint16_t *flags)
105 {
106 if (opt_id == O_ZONE_ORIG)
107 *flags |= XT_CT_ZONE_DIR_ORIG;
108 if (opt_id == O_ZONE_REPLY)
109 *flags |= XT_CT_ZONE_DIR_REPL;
110
111 *zone_id = 0;
112
113 if (strcasecmp(opt, "mark") == 0) {
114 *flags |= XT_CT_ZONE_MARK;
115 } else {
116 uintmax_t val;
117
118 if (!xtables_strtoul(opt, NULL, &val, 0, UINT16_MAX))
119 xtables_error(PARAMETER_PROBLEM,
120 "Cannot parse %s as a zone ID\n", opt);
121
122 *zone_id = (uint16_t)val;
123 }
124 }
125
ct_print_zone_id(const char * pfx,uint16_t zone_id,uint16_t flags)126 static void ct_print_zone_id(const char *pfx, uint16_t zone_id, uint16_t flags)
127 {
128 printf(" %s", pfx);
129
130 if ((flags & (XT_CT_ZONE_DIR_ORIG |
131 XT_CT_ZONE_DIR_REPL)) == XT_CT_ZONE_DIR_ORIG)
132 printf("-orig");
133 if ((flags & (XT_CT_ZONE_DIR_ORIG |
134 XT_CT_ZONE_DIR_REPL)) == XT_CT_ZONE_DIR_REPL)
135 printf("-reply");
136 if (flags & XT_CT_ZONE_MARK)
137 printf(" mark");
138 else
139 printf(" %u", zone_id);
140 }
141
ct_parse_events(const struct event_tbl * tbl,unsigned int size,const char * events)142 static uint32_t ct_parse_events(const struct event_tbl *tbl, unsigned int size,
143 const char *events)
144 {
145 char str[strlen(events) + 1], *e = str, *t;
146 unsigned int mask = 0, i;
147
148 strcpy(str, events);
149 while ((t = strsep(&e, ","))) {
150 for (i = 0; i < size; i++) {
151 if (strcmp(t, tbl[i].name))
152 continue;
153 mask |= 1 << tbl[i].event;
154 break;
155 }
156
157 if (i == size)
158 xtables_error(PARAMETER_PROBLEM, "Unknown event type \"%s\"", t);
159 }
160
161 return mask;
162 }
163
ct_print_events(const char * pfx,const struct event_tbl * tbl,unsigned int size,uint32_t mask)164 static void ct_print_events(const char *pfx, const struct event_tbl *tbl,
165 unsigned int size, uint32_t mask)
166 {
167 const char *sep = "";
168 unsigned int i;
169
170 printf(" %s ", pfx);
171 for (i = 0; i < size; i++) {
172 if (mask & (1 << tbl[i].event)) {
173 printf("%s%s", sep, tbl[i].name);
174 sep = ",";
175 }
176 }
177 }
178
ct_parse(struct xt_option_call * cb)179 static void ct_parse(struct xt_option_call *cb)
180 {
181 struct xt_ct_target_info *info = cb->data;
182
183 xtables_option_parse(cb);
184 switch (cb->entry->id) {
185 case O_NOTRACK:
186 info->flags |= XT_CT_NOTRACK;
187 break;
188 case O_ZONE_ORIG:
189 case O_ZONE_REPLY:
190 case O_ZONE:
191 ct_parse_zone_id(cb->arg, cb->entry->id, &info->zone,
192 &info->flags);
193 break;
194 case O_CTEVENTS:
195 info->ct_events = ct_parse_events(ct_event_tbl, ARRAY_SIZE(ct_event_tbl), cb->arg);
196 break;
197 case O_EXPEVENTS:
198 info->exp_events = ct_parse_events(exp_event_tbl, ARRAY_SIZE(exp_event_tbl), cb->arg);
199 break;
200 }
201 }
202
ct_parse_v1(struct xt_option_call * cb)203 static void ct_parse_v1(struct xt_option_call *cb)
204 {
205 struct xt_ct_target_info_v1 *info = cb->data;
206
207 xtables_option_parse(cb);
208 switch (cb->entry->id) {
209 case O_NOTRACK:
210 info->flags |= XT_CT_NOTRACK;
211 break;
212 case O_ZONE_ORIG:
213 case O_ZONE_REPLY:
214 case O_ZONE:
215 ct_parse_zone_id(cb->arg, cb->entry->id, &info->zone,
216 &info->flags);
217 break;
218 case O_CTEVENTS:
219 info->ct_events = ct_parse_events(ct_event_tbl,
220 ARRAY_SIZE(ct_event_tbl),
221 cb->arg);
222 break;
223 case O_EXPEVENTS:
224 info->exp_events = ct_parse_events(exp_event_tbl,
225 ARRAY_SIZE(exp_event_tbl),
226 cb->arg);
227 break;
228 }
229 }
230
ct_print(const void * ip,const struct xt_entry_target * target,int numeric)231 static void ct_print(const void *ip, const struct xt_entry_target *target, int numeric)
232 {
233 const struct xt_ct_target_info *info =
234 (const struct xt_ct_target_info *)target->data;
235
236 printf(" CT");
237 if (info->flags & XT_CT_NOTRACK)
238 printf(" notrack");
239 if (info->helper[0])
240 printf(" helper %s", info->helper);
241 if (info->ct_events)
242 ct_print_events("ctevents", ct_event_tbl,
243 ARRAY_SIZE(ct_event_tbl), info->ct_events);
244 if (info->exp_events)
245 ct_print_events("expevents", exp_event_tbl,
246 ARRAY_SIZE(exp_event_tbl), info->exp_events);
247 if (info->flags & XT_CT_ZONE_MARK || info->zone)
248 ct_print_zone_id("zone", info->zone, info->flags);
249 }
250
251 static void
ct_print_v1(const void * ip,const struct xt_entry_target * target,int numeric)252 ct_print_v1(const void *ip, const struct xt_entry_target *target, int numeric)
253 {
254 const struct xt_ct_target_info_v1 *info =
255 (const struct xt_ct_target_info_v1 *)target->data;
256
257 if (info->flags & XT_CT_NOTRACK_ALIAS) {
258 printf (" NOTRACK");
259 return;
260 }
261 printf(" CT");
262 if (info->flags & XT_CT_NOTRACK)
263 printf(" notrack");
264 if (info->helper[0])
265 printf(" helper %s", info->helper);
266 if (info->timeout[0])
267 printf(" timeout %s", info->timeout);
268 if (info->ct_events)
269 ct_print_events("ctevents", ct_event_tbl,
270 ARRAY_SIZE(ct_event_tbl), info->ct_events);
271 if (info->exp_events)
272 ct_print_events("expevents", exp_event_tbl,
273 ARRAY_SIZE(exp_event_tbl), info->exp_events);
274 if (info->flags & XT_CT_ZONE_MARK || info->zone)
275 ct_print_zone_id("zone", info->zone, info->flags);
276 }
277
ct_save(const void * ip,const struct xt_entry_target * target)278 static void ct_save(const void *ip, const struct xt_entry_target *target)
279 {
280 const struct xt_ct_target_info *info =
281 (const struct xt_ct_target_info *)target->data;
282
283 if (info->flags & XT_CT_NOTRACK_ALIAS)
284 return;
285 if (info->flags & XT_CT_NOTRACK)
286 printf(" --notrack");
287 if (info->helper[0])
288 printf(" --helper %s", info->helper);
289 if (info->ct_events)
290 ct_print_events("--ctevents", ct_event_tbl,
291 ARRAY_SIZE(ct_event_tbl), info->ct_events);
292 if (info->exp_events)
293 ct_print_events("--expevents", exp_event_tbl,
294 ARRAY_SIZE(exp_event_tbl), info->exp_events);
295 if (info->flags & XT_CT_ZONE_MARK || info->zone)
296 ct_print_zone_id("--zone", info->zone, info->flags);
297 }
298
ct_save_v1(const void * ip,const struct xt_entry_target * target)299 static void ct_save_v1(const void *ip, const struct xt_entry_target *target)
300 {
301 const struct xt_ct_target_info_v1 *info =
302 (const struct xt_ct_target_info_v1 *)target->data;
303
304 if (info->flags & XT_CT_NOTRACK_ALIAS)
305 return;
306 if (info->flags & XT_CT_NOTRACK)
307 printf(" --notrack");
308 if (info->helper[0])
309 printf(" --helper %s", info->helper);
310 if (info->timeout[0])
311 printf(" --timeout %s", info->timeout);
312 if (info->ct_events)
313 ct_print_events("--ctevents", ct_event_tbl,
314 ARRAY_SIZE(ct_event_tbl), info->ct_events);
315 if (info->exp_events)
316 ct_print_events("--expevents", exp_event_tbl,
317 ARRAY_SIZE(exp_event_tbl), info->exp_events);
318 if (info->flags & XT_CT_ZONE_MARK || info->zone)
319 ct_print_zone_id("--zone", info->zone, info->flags);
320 }
321
322 static const char *
ct_print_name_alias(const struct xt_entry_target * target)323 ct_print_name_alias(const struct xt_entry_target *target)
324 {
325 struct xt_ct_target_info *info = (void *)target->data;
326
327 return info->flags & XT_CT_NOTRACK_ALIAS ? "NOTRACK" : "CT";
328 }
329
notrack_ct0_tg_init(struct xt_entry_target * target)330 static void notrack_ct0_tg_init(struct xt_entry_target *target)
331 {
332 struct xt_ct_target_info *info = (void *)target->data;
333
334 info->flags = XT_CT_NOTRACK;
335 }
336
notrack_ct1_tg_init(struct xt_entry_target * target)337 static void notrack_ct1_tg_init(struct xt_entry_target *target)
338 {
339 struct xt_ct_target_info_v1 *info = (void *)target->data;
340
341 info->flags = XT_CT_NOTRACK;
342 }
343
notrack_ct2_tg_init(struct xt_entry_target * target)344 static void notrack_ct2_tg_init(struct xt_entry_target *target)
345 {
346 struct xt_ct_target_info_v1 *info = (void *)target->data;
347
348 info->flags = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS;
349 }
350
xlate_ct1_tg(struct xt_xlate * xl,const struct xt_xlate_tg_params * params)351 static int xlate_ct1_tg(struct xt_xlate *xl,
352 const struct xt_xlate_tg_params *params)
353 {
354 struct xt_ct_target_info_v1 *info =
355 (struct xt_ct_target_info_v1 *)params->target->data;
356
357 if (info->flags & XT_CT_NOTRACK)
358 xt_xlate_add(xl, "notrack");
359 else
360 return 0;
361
362 return 1;
363 }
364
365 static struct xtables_target ct_target_reg[] = {
366 {
367 .family = NFPROTO_UNSPEC,
368 .name = "CT",
369 .version = XTABLES_VERSION,
370 .size = XT_ALIGN(sizeof(struct xt_ct_target_info)),
371 .userspacesize = offsetof(struct xt_ct_target_info, ct),
372 .help = ct_help,
373 .print = ct_print,
374 .save = ct_save,
375 .x6_parse = ct_parse,
376 .x6_options = ct_opts,
377 },
378 {
379 .family = NFPROTO_UNSPEC,
380 .name = "CT",
381 .revision = 1,
382 .version = XTABLES_VERSION,
383 .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
384 .userspacesize = offsetof(struct xt_ct_target_info_v1, ct),
385 .help = ct_help_v1,
386 .print = ct_print_v1,
387 .save = ct_save_v1,
388 .x6_parse = ct_parse_v1,
389 .x6_options = ct_opts_v1,
390 },
391 {
392 .family = NFPROTO_UNSPEC,
393 .name = "CT",
394 .revision = 2,
395 .version = XTABLES_VERSION,
396 .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
397 .userspacesize = offsetof(struct xt_ct_target_info_v1, ct),
398 .help = ct_help_v1,
399 .print = ct_print_v1,
400 .save = ct_save_v1,
401 .alias = ct_print_name_alias,
402 .x6_parse = ct_parse_v1,
403 .x6_options = ct_opts_v1,
404 .xlate = xlate_ct1_tg,
405 },
406 {
407 .family = NFPROTO_UNSPEC,
408 .name = "NOTRACK",
409 .real_name = "CT",
410 .revision = 0,
411 .version = XTABLES_VERSION,
412 .size = XT_ALIGN(sizeof(struct xt_ct_target_info)),
413 .userspacesize = offsetof(struct xt_ct_target_info, ct),
414 .init = notrack_ct0_tg_init,
415 },
416 {
417 .family = NFPROTO_UNSPEC,
418 .name = "NOTRACK",
419 .real_name = "CT",
420 .revision = 1,
421 .version = XTABLES_VERSION,
422 .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
423 .userspacesize = offsetof(struct xt_ct_target_info_v1, ct),
424 .init = notrack_ct1_tg_init,
425 },
426 {
427 .family = NFPROTO_UNSPEC,
428 .name = "NOTRACK",
429 .real_name = "CT",
430 .revision = 2,
431 .ext_flags = XTABLES_EXT_ALIAS,
432 .version = XTABLES_VERSION,
433 .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
434 .userspacesize = offsetof(struct xt_ct_target_info_v1, ct),
435 .init = notrack_ct2_tg_init,
436 .xlate = xlate_ct1_tg,
437 },
438 {
439 .family = NFPROTO_UNSPEC,
440 .name = "NOTRACK",
441 .revision = 0,
442 .version = XTABLES_VERSION,
443 },
444 };
445
_init(void)446 void _init(void)
447 {
448 xtables_register_targets(ct_target_reg, ARRAY_SIZE(ct_target_reg));
449 }
450