blob: d1164c5e9f0e6e50aa3bab969e389cb68ed8867e [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
2 * libxt_conntrack
3 * Shared library add-on to iptables for conntrack matching support.
4 *
5 * GPL (C) 2001 Marc Boucher (marc@mbsi.ca).
6 * Copyright © CC Computer Consultants GmbH, 2007 - 2008
7 * Jan Engelhardt <jengelh@computergmbh.de>
8 */
9#include <sys/socket.h>
10#include <sys/types.h>
11#include <ctype.h>
12#include <getopt.h>
13#include <netdb.h>
14#include <stdbool.h>
15#include <stddef.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <xtables.h>
20#include <linux/netfilter.h>
21#include <linux/netfilter/xt_conntrack.h>
22#include <linux/netfilter/nf_conntrack_common.h>
23#include <arpa/inet.h>
24
25static void conntrack_mt_help(void)
26{
27 printf(
28"conntrack match options:\n"
29"[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n"
30" State(s) to match\n"
31"[!] --ctproto proto Protocol to match; by number or name, e.g. \"tcp\"\n"
32"[!] --ctorigsrc address[/mask]\n"
33"[!] --ctorigdst address[/mask]\n"
34"[!] --ctreplsrc address[/mask]\n"
35"[!] --ctrepldst address[/mask]\n"
36" Original/Reply source/destination address\n"
37"[!] --ctorigsrcport port\n"
38"[!] --ctorigdstport port\n"
39"[!] --ctreplsrcport port\n"
40"[!] --ctrepldstport port\n"
41" TCP/UDP/SCTP orig./reply source/destination port\n"
42"[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n"
43" Status(es) to match\n"
44"[!] --ctexpire time[:time] Match remaining lifetime in seconds against\n"
45" value or range of values (inclusive)\n"
46" --ctdir {ORIGINAL|REPLY} Flow direction of packet\n");
47}
48
49static const struct option conntrack_mt_opts_v0[] = {
50 {.name = "ctstate", .has_arg = true, .val = '1'},
51 {.name = "ctproto", .has_arg = true, .val = '2'},
52 {.name = "ctorigsrc", .has_arg = true, .val = '3'},
53 {.name = "ctorigdst", .has_arg = true, .val = '4'},
54 {.name = "ctreplsrc", .has_arg = true, .val = '5'},
55 {.name = "ctrepldst", .has_arg = true, .val = '6'},
56 {.name = "ctstatus", .has_arg = true, .val = '7'},
57 {.name = "ctexpire", .has_arg = true, .val = '8'},
58 { .name = NULL }
59};
60
61static const struct option conntrack_mt_opts[] = {
62 {.name = "ctstate", .has_arg = true, .val = '1'},
63 {.name = "ctproto", .has_arg = true, .val = '2'},
64 {.name = "ctorigsrc", .has_arg = true, .val = '3'},
65 {.name = "ctorigdst", .has_arg = true, .val = '4'},
66 {.name = "ctreplsrc", .has_arg = true, .val = '5'},
67 {.name = "ctrepldst", .has_arg = true, .val = '6'},
68 {.name = "ctstatus", .has_arg = true, .val = '7'},
69 {.name = "ctexpire", .has_arg = true, .val = '8'},
70 {.name = "ctorigsrcport", .has_arg = true, .val = 'a'},
71 {.name = "ctorigdstport", .has_arg = true, .val = 'b'},
72 {.name = "ctreplsrcport", .has_arg = true, .val = 'c'},
73 {.name = "ctrepldstport", .has_arg = true, .val = 'd'},
74 {.name = "ctdir", .has_arg = true, .val = 'e'},
75 {.name = NULL},
76};
77
78static int
79parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo)
80{
81 if (strncasecmp(state, "INVALID", len) == 0)
82 sinfo->statemask |= XT_CONNTRACK_STATE_INVALID;
83 else if (strncasecmp(state, "NEW", len) == 0)
84 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
85 else if (strncasecmp(state, "ESTABLISHED", len) == 0)
86 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
87 else if (strncasecmp(state, "RELATED", len) == 0)
88 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
89 else if (strncasecmp(state, "UNTRACKED", len) == 0)
90 sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED;
91 else if (strncasecmp(state, "SNAT", len) == 0)
92 sinfo->statemask |= XT_CONNTRACK_STATE_SNAT;
93 else if (strncasecmp(state, "DNAT", len) == 0)
94 sinfo->statemask |= XT_CONNTRACK_STATE_DNAT;
95 else
96 return 0;
97 return 1;
98}
99
100static void
101parse_states(const char *arg, struct xt_conntrack_info *sinfo)
102{
103 const char *comma;
104
105 while ((comma = strchr(arg, ',')) != NULL) {
106 if (comma == arg || !parse_state(arg, comma-arg, sinfo))
107 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
108 arg = comma+1;
109 }
110 if (!*arg)
111 xtables_error(PARAMETER_PROBLEM, "\"--ctstate\" requires a list of "
112 "states with no spaces, e.g. "
113 "ESTABLISHED,RELATED");
114 if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
115 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
116}
117
118static bool
119conntrack_ps_state(struct xt_conntrack_mtinfo2 *info, const char *state,
120 size_t z)
121{
122 if (strncasecmp(state, "INVALID", z) == 0)
123 info->state_mask |= XT_CONNTRACK_STATE_INVALID;
124 else if (strncasecmp(state, "NEW", z) == 0)
125 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
126 else if (strncasecmp(state, "ESTABLISHED", z) == 0)
127 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
128 else if (strncasecmp(state, "RELATED", z) == 0)
129 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
130 else if (strncasecmp(state, "UNTRACKED", z) == 0)
131 info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED;
132 else if (strncasecmp(state, "SNAT", z) == 0)
133 info->state_mask |= XT_CONNTRACK_STATE_SNAT;
134 else if (strncasecmp(state, "DNAT", z) == 0)
135 info->state_mask |= XT_CONNTRACK_STATE_DNAT;
136 else
137 return false;
138 return true;
139}
140
141static void
142conntrack_ps_states(struct xt_conntrack_mtinfo2 *info, const char *arg)
143{
144 const char *comma;
145
146 while ((comma = strchr(arg, ',')) != NULL) {
147 if (comma == arg || !conntrack_ps_state(info, arg, comma - arg))
148 xtables_error(PARAMETER_PROBLEM,
149 "Bad ctstate \"%s\"", arg);
150 arg = comma + 1;
151 }
152
153 if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg)))
154 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
155}
156
157static int
158parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo)
159{
160 if (strncasecmp(status, "NONE", len) == 0)
161 sinfo->statusmask |= 0;
162 else if (strncasecmp(status, "EXPECTED", len) == 0)
163 sinfo->statusmask |= IPS_EXPECTED;
164 else if (strncasecmp(status, "SEEN_REPLY", len) == 0)
165 sinfo->statusmask |= IPS_SEEN_REPLY;
166 else if (strncasecmp(status, "ASSURED", len) == 0)
167 sinfo->statusmask |= IPS_ASSURED;
168#ifdef IPS_CONFIRMED
169 else if (strncasecmp(status, "CONFIRMED", len) == 0)
170 sinfo->statusmask |= IPS_CONFIRMED;
171#endif
172 else
173 return 0;
174 return 1;
175}
176
177static void
178parse_statuses(const char *arg, struct xt_conntrack_info *sinfo)
179{
180 const char *comma;
181
182 while ((comma = strchr(arg, ',')) != NULL) {
183 if (comma == arg || !parse_status(arg, comma-arg, sinfo))
184 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
185 arg = comma+1;
186 }
187
188 if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
189 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
190}
191
192static bool
193conntrack_ps_status(struct xt_conntrack_mtinfo2 *info, const char *status,
194 size_t z)
195{
196 if (strncasecmp(status, "NONE", z) == 0)
197 info->status_mask |= 0;
198 else if (strncasecmp(status, "EXPECTED", z) == 0)
199 info->status_mask |= IPS_EXPECTED;
200 else if (strncasecmp(status, "SEEN_REPLY", z) == 0)
201 info->status_mask |= IPS_SEEN_REPLY;
202 else if (strncasecmp(status, "ASSURED", z) == 0)
203 info->status_mask |= IPS_ASSURED;
204 else if (strncasecmp(status, "CONFIRMED", z) == 0)
205 info->status_mask |= IPS_CONFIRMED;
206 else
207 return false;
208 return true;
209}
210
211static void
212conntrack_ps_statuses(struct xt_conntrack_mtinfo2 *info, const char *arg)
213{
214 const char *comma;
215
216 while ((comma = strchr(arg, ',')) != NULL) {
217 if (comma == arg || !conntrack_ps_status(info, arg, comma - arg))
218 xtables_error(PARAMETER_PROBLEM,
219 "Bad ctstatus \"%s\"", arg);
220 arg = comma + 1;
221 }
222
223 if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg)))
224 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
225}
226
227static unsigned long
228parse_expire(const char *s)
229{
230 unsigned int len;
231
232 if (!xtables_strtoui(s, NULL, &len, 0, UINT32_MAX))
233 xtables_error(PARAMETER_PROBLEM, "expire value invalid: \"%s\"\n", s);
234 else
235 return len;
236}
237
238/* If a single value is provided, min and max are both set to the value */
239static void
240parse_expires(const char *s, struct xt_conntrack_info *sinfo)
241{
242 char *buffer;
243 char *cp;
244
245 buffer = strdup(s);
246 if ((cp = strchr(buffer, ':')) == NULL)
247 sinfo->expires_min = sinfo->expires_max =
248 parse_expire(buffer);
249 else {
250 *cp = '\0';
251 cp++;
252
253 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
254 sinfo->expires_max = cp[0]
255 ? parse_expire(cp)
256 : (unsigned long)-1;
257 }
258 free(buffer);
259
260 if (sinfo->expires_min > sinfo->expires_max)
261 xtables_error(PARAMETER_PROBLEM,
262 "expire min. range value `%lu' greater than max. "
263 "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
264}
265
266static void
267conntrack_ps_expires(struct xt_conntrack_mtinfo2 *info, const char *s)
268{
269 unsigned int min, max;
270 char *end;
271
272 if (!xtables_strtoui(s, &end, &min, 0, UINT32_MAX))
273 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);
274 max = min;
275 if (*end == ':')
276 if (!xtables_strtoui(s, &end, &max, 0, UINT32_MAX))
277 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);
278 if (*end != '\0')
279 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);
280
281 if (min > max)
282 xtables_error(PARAMETER_PROBLEM,
283 "expire min. range value \"%u\" greater than max. "
284 "range value \"%u\"", min, max);
285
286 info->expires_min = min;
287 info->expires_max = max;
288}
289
290static int conntrack_parse(int c, char **argv, int invert, unsigned int *flags,
291 const void *entry, struct xt_entry_match **match)
292{
293 struct xt_conntrack_info *sinfo = (void *)(*match)->data;
294 char *protocol = NULL;
295 unsigned int naddrs = 0;
296 struct in_addr *addrs = NULL;
297
298
299 switch (c) {
300 case '1':
301 xtables_check_inverse(optarg, &invert, &optind, 0);
302
303 parse_states(argv[optind-1], sinfo);
304 if (invert) {
305 sinfo->invflags |= XT_CONNTRACK_STATE;
306 }
307 sinfo->flags |= XT_CONNTRACK_STATE;
308 break;
309
310 case '2':
311 xtables_check_inverse(optarg, &invert, &optind, 0);
312
313 if(invert)
314 sinfo->invflags |= XT_CONNTRACK_PROTO;
315
316 /* Canonicalize into lower case */
317 for (protocol = argv[optind-1]; *protocol; protocol++)
318 *protocol = tolower(*protocol);
319
320 protocol = argv[optind-1];
321 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum =
322 xtables_parse_protocol(protocol);
323
324 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
325 && (sinfo->invflags & XT_INV_PROTO))
326 xtables_error(PARAMETER_PROBLEM,
327 "rule would never match protocol");
328
329 sinfo->flags |= XT_CONNTRACK_PROTO;
330 break;
331
332 case '3':
333 xtables_check_inverse(optarg, &invert, &optind, 0);
334
335 if (invert)
336 sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
337
338 xtables_ipparse_any(argv[optind-1], &addrs,
339 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
340 &naddrs);
341 if(naddrs > 1)
342 xtables_error(PARAMETER_PROBLEM,
343 "multiple IP addresses not allowed");
344
345 if(naddrs == 1) {
346 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
347 }
348
349 sinfo->flags |= XT_CONNTRACK_ORIGSRC;
350 break;
351
352 case '4':
353 xtables_check_inverse(optarg, &invert, &optind, 0);
354
355 if (invert)
356 sinfo->invflags |= XT_CONNTRACK_ORIGDST;
357
358 xtables_ipparse_any(argv[optind-1], &addrs,
359 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
360 &naddrs);
361 if(naddrs > 1)
362 xtables_error(PARAMETER_PROBLEM,
363 "multiple IP addresses not allowed");
364
365 if(naddrs == 1) {
366 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
367 }
368
369 sinfo->flags |= XT_CONNTRACK_ORIGDST;
370 break;
371
372 case '5':
373 xtables_check_inverse(optarg, &invert, &optind, 0);
374
375 if (invert)
376 sinfo->invflags |= XT_CONNTRACK_REPLSRC;
377
378 xtables_ipparse_any(argv[optind-1], &addrs,
379 &sinfo->sipmsk[IP_CT_DIR_REPLY],
380 &naddrs);
381 if(naddrs > 1)
382 xtables_error(PARAMETER_PROBLEM,
383 "multiple IP addresses not allowed");
384
385 if(naddrs == 1) {
386 sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
387 }
388
389 sinfo->flags |= XT_CONNTRACK_REPLSRC;
390 break;
391
392 case '6':
393 xtables_check_inverse(optarg, &invert, &optind, 0);
394
395 if (invert)
396 sinfo->invflags |= XT_CONNTRACK_REPLDST;
397
398 xtables_ipparse_any(argv[optind-1], &addrs,
399 &sinfo->dipmsk[IP_CT_DIR_REPLY],
400 &naddrs);
401 if(naddrs > 1)
402 xtables_error(PARAMETER_PROBLEM,
403 "multiple IP addresses not allowed");
404
405 if(naddrs == 1) {
406 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
407 }
408
409 sinfo->flags |= XT_CONNTRACK_REPLDST;
410 break;
411
412 case '7':
413 xtables_check_inverse(optarg, &invert, &optind, 0);
414
415 parse_statuses(argv[optind-1], sinfo);
416 if (invert) {
417 sinfo->invflags |= XT_CONNTRACK_STATUS;
418 }
419 sinfo->flags |= XT_CONNTRACK_STATUS;
420 break;
421
422 case '8':
423 xtables_check_inverse(optarg, &invert, &optind, 0);
424
425 parse_expires(argv[optind-1], sinfo);
426 if (invert) {
427 sinfo->invflags |= XT_CONNTRACK_EXPIRES;
428 }
429 sinfo->flags |= XT_CONNTRACK_EXPIRES;
430 break;
431
432 default:
433 return 0;
434 }
435
436 *flags = sinfo->flags;
437 return 1;
438}
439
440static int
441conntrack_mt_parse(int c, bool invert, unsigned int *flags,
442 struct xt_conntrack_mtinfo2 *info)
443{
444 unsigned int port;
445 char *p;
446
447 switch (c) {
448 case '1': /* --ctstate */
449 conntrack_ps_states(info, optarg);
450 info->match_flags |= XT_CONNTRACK_STATE;
451 if (invert)
452 info->invert_flags |= XT_CONNTRACK_STATE;
453 break;
454
455 case '2': /* --ctproto */
456 /* Canonicalize into lower case */
457 for (p = optarg; *p != '\0'; ++p)
458 *p = tolower(*p);
459 info->l4proto = xtables_parse_protocol(optarg);
460
461 if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
462 xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
463 "never match protocol");
464
465 info->match_flags |= XT_CONNTRACK_PROTO;
466 if (invert)
467 info->invert_flags |= XT_CONNTRACK_PROTO;
468 break;
469
470 case '7': /* --ctstatus */
471 conntrack_ps_statuses(info, optarg);
472 info->match_flags |= XT_CONNTRACK_STATUS;
473 if (invert)
474 info->invert_flags |= XT_CONNTRACK_STATUS;
475 break;
476
477 case '8': /* --ctexpire */
478 conntrack_ps_expires(info, optarg);
479 info->match_flags |= XT_CONNTRACK_EXPIRES;
480 if (invert)
481 info->invert_flags |= XT_CONNTRACK_EXPIRES;
482 break;
483
484 case 'a': /* --ctorigsrcport */
485 if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX))
486 xtables_param_act(XTF_BAD_VALUE, "conntrack",
487 "--ctorigsrcport", optarg);
488 info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
489 info->origsrc_port = htons(port);
490 if (invert)
491 info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
492 break;
493
494 case 'b': /* --ctorigdstport */
495 if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX))
496 xtables_param_act(XTF_BAD_VALUE, "conntrack",
497 "--ctorigdstport", optarg);
498 info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
499 info->origdst_port = htons(port);
500 if (invert)
501 info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
502 break;
503
504 case 'c': /* --ctreplsrcport */
505 if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX))
506 xtables_param_act(XTF_BAD_VALUE, "conntrack",
507 "--ctreplsrcport", optarg);
508 info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
509 info->replsrc_port = htons(port);
510 if (invert)
511 info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
512 break;
513
514 case 'd': /* --ctrepldstport */
515 if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX))
516 xtables_param_act(XTF_BAD_VALUE, "conntrack",
517 "--ctrepldstport", optarg);
518 info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
519 info->repldst_port = htons(port);
520 if (invert)
521 info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
522 break;
523
524 case 'e': /* --ctdir */
525 xtables_param_act(XTF_NO_INVERT, "conntrack", "--ctdir", invert);
526 if (strcasecmp(optarg, "ORIGINAL") == 0) {
527 info->match_flags |= XT_CONNTRACK_DIRECTION;
528 info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
529 } else if (strcasecmp(optarg, "REPLY") == 0) {
530 info->match_flags |= XT_CONNTRACK_DIRECTION;
531 info->invert_flags |= XT_CONNTRACK_DIRECTION;
532 } else {
533 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", optarg);
534 }
535 break;
536
537 default:
538 return false;
539 }
540
541 *flags = info->match_flags;
542 return true;
543}
544
545static int
546conntrack_mt4_parse(int c, bool invert, unsigned int *flags,
547 struct xt_conntrack_mtinfo2 *info)
548{
549 struct in_addr *addr = NULL;
550 unsigned int naddrs = 0;
551
552 switch (c) {
553 case '3': /* --ctorigsrc */
554 xtables_ipparse_any(optarg, &addr, &info->origsrc_mask.in,
555 &naddrs);
556 if (naddrs > 1)
557 xtables_error(PARAMETER_PROBLEM,
558 "multiple IP addresses not allowed");
559 if (naddrs == 1)
560 memcpy(&info->origsrc_addr.in, addr, sizeof(*addr));
561 info->match_flags |= XT_CONNTRACK_ORIGSRC;
562 if (invert)
563 info->invert_flags |= XT_CONNTRACK_ORIGSRC;
564 break;
565
566 case '4': /* --ctorigdst */
567 xtables_ipparse_any(optarg, &addr, &info->origdst_mask.in,
568 &naddrs);
569 if (naddrs > 1)
570 xtables_error(PARAMETER_PROBLEM,
571 "multiple IP addresses not allowed");
572 if (naddrs == 1)
573 memcpy(&info->origdst_addr.in, addr, sizeof(*addr));
574 info->match_flags |= XT_CONNTRACK_ORIGDST;
575 if (invert)
576 info->invert_flags |= XT_CONNTRACK_ORIGDST;
577 break;
578
579 case '5': /* --ctreplsrc */
580 xtables_ipparse_any(optarg, &addr, &info->replsrc_mask.in,
581 &naddrs);
582 if (naddrs > 1)
583 xtables_error(PARAMETER_PROBLEM,
584 "multiple IP addresses not allowed");
585 if (naddrs == 1)
586 memcpy(&info->replsrc_addr.in, addr, sizeof(*addr));
587 info->match_flags |= XT_CONNTRACK_REPLSRC;
588 if (invert)
589 info->invert_flags |= XT_CONNTRACK_REPLSRC;
590 break;
591
592 case '6': /* --ctrepldst */
593 xtables_ipparse_any(optarg, &addr, &info->repldst_mask.in,
594 &naddrs);
595 if (naddrs > 1)
596 xtables_error(PARAMETER_PROBLEM,
597 "multiple IP addresses not allowed");
598 if (naddrs == 1)
599 memcpy(&info->repldst_addr.in, addr, sizeof(*addr));
600 info->match_flags |= XT_CONNTRACK_REPLDST;
601 if (invert)
602 info->invert_flags |= XT_CONNTRACK_REPLDST;
603 break;
604
605
606 default:
607 return conntrack_mt_parse(c, invert, flags, info);
608 }
609
610 *flags = info->match_flags;
611 return true;
612}
613
614static int
615conntrack_mt6_parse(int c, bool invert, unsigned int *flags,
616 struct xt_conntrack_mtinfo2 *info)
617{
618 struct in6_addr *addr = NULL;
619 unsigned int naddrs = 0;
620
621 switch (c) {
622 case '3': /* --ctorigsrc */
623 xtables_ip6parse_any(optarg, &addr,
624 &info->origsrc_mask.in6, &naddrs);
625 if (naddrs > 1)
626 xtables_error(PARAMETER_PROBLEM,
627 "multiple IP addresses not allowed");
628 if (naddrs == 1)
629 memcpy(&info->origsrc_addr.in6, addr, sizeof(*addr));
630 info->match_flags |= XT_CONNTRACK_ORIGSRC;
631 if (invert)
632 info->invert_flags |= XT_CONNTRACK_ORIGSRC;
633 break;
634
635 case '4': /* --ctorigdst */
636 xtables_ip6parse_any(optarg, &addr,
637 &info->origdst_mask.in6, &naddrs);
638 if (naddrs > 1)
639 xtables_error(PARAMETER_PROBLEM,
640 "multiple IP addresses not allowed");
641 if (naddrs == 1)
642 memcpy(&info->origdst_addr.in, addr, sizeof(*addr));
643 info->match_flags |= XT_CONNTRACK_ORIGDST;
644 if (invert)
645 info->invert_flags |= XT_CONNTRACK_ORIGDST;
646 break;
647
648 case '5': /* --ctreplsrc */
649 xtables_ip6parse_any(optarg, &addr,
650 &info->replsrc_mask.in6, &naddrs);
651 if (naddrs > 1)
652 xtables_error(PARAMETER_PROBLEM,
653 "multiple IP addresses not allowed");
654 if (naddrs == 1)
655 memcpy(&info->replsrc_addr.in, addr, sizeof(*addr));
656 info->match_flags |= XT_CONNTRACK_REPLSRC;
657 if (invert)
658 info->invert_flags |= XT_CONNTRACK_REPLSRC;
659 break;
660
661 case '6': /* --ctrepldst */
662 xtables_ip6parse_any(optarg, &addr,
663 &info->repldst_mask.in6, &naddrs);
664 if (naddrs > 1)
665 xtables_error(PARAMETER_PROBLEM,
666 "multiple IP addresses not allowed");
667 if (naddrs == 1)
668 memcpy(&info->repldst_addr.in, addr, sizeof(*addr));
669 info->match_flags |= XT_CONNTRACK_REPLDST;
670 if (invert)
671 info->invert_flags |= XT_CONNTRACK_REPLDST;
672 break;
673
674
675 default:
676 return conntrack_mt_parse(c, invert, flags, info);
677 }
678
679 *flags = info->match_flags;
680 return true;
681}
682
683#define cinfo_transform(r, l) \
684 do { \
685 memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
686 (r)->state_mask = (l)->state_mask; \
687 (r)->status_mask = (l)->status_mask; \
688 } while (false);
689
690static int
691conntrack1_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
692 const void *entry, struct xt_entry_match **match)
693{
694 struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
695 struct xt_conntrack_mtinfo2 up;
696
697 cinfo_transform(&up, info);
698 if (!conntrack_mt4_parse(c, invert, flags, &up))
699 return false;
700 cinfo_transform(info, &up);
701 return true;
702}
703
704static int
705conntrack1_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
706 const void *entry, struct xt_entry_match **match)
707{
708 struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
709 struct xt_conntrack_mtinfo2 up;
710
711 cinfo_transform(&up, info);
712 if (!conntrack_mt6_parse(c, invert, flags, &up))
713 return false;
714 cinfo_transform(info, &up);
715 return true;
716}
717
718static int
719conntrack2_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
720 const void *entry, struct xt_entry_match **match)
721{
722 return conntrack_mt4_parse(c, invert, flags, (void *)(*match)->data);
723}
724
725static int
726conntrack2_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
727 const void *entry, struct xt_entry_match **match)
728{
729 return conntrack_mt6_parse(c, invert, flags, (void *)(*match)->data);
730}
731
732static void conntrack_mt_check(unsigned int flags)
733{
734 if (flags == 0)
735 xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
736 "is required");
737}
738
739static void
740print_state(unsigned int statemask)
741{
742 const char *sep = "";
743
744 if (statemask & XT_CONNTRACK_STATE_INVALID) {
745 printf("%sINVALID", sep);
746 sep = ",";
747 }
748 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
749 printf("%sNEW", sep);
750 sep = ",";
751 }
752 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
753 printf("%sRELATED", sep);
754 sep = ",";
755 }
756 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
757 printf("%sESTABLISHED", sep);
758 sep = ",";
759 }
760 if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
761 printf("%sUNTRACKED", sep);
762 sep = ",";
763 }
764 if (statemask & XT_CONNTRACK_STATE_SNAT) {
765 printf("%sSNAT", sep);
766 sep = ",";
767 }
768 if (statemask & XT_CONNTRACK_STATE_DNAT) {
769 printf("%sDNAT", sep);
770 sep = ",";
771 }
772 printf(" ");
773}
774
775static void
776print_status(unsigned int statusmask)
777{
778 const char *sep = "";
779
780 if (statusmask & IPS_EXPECTED) {
781 printf("%sEXPECTED", sep);
782 sep = ",";
783 }
784 if (statusmask & IPS_SEEN_REPLY) {
785 printf("%sSEEN_REPLY", sep);
786 sep = ",";
787 }
788 if (statusmask & IPS_ASSURED) {
789 printf("%sASSURED", sep);
790 sep = ",";
791 }
792 if (statusmask & IPS_CONFIRMED) {
793 printf("%sCONFIRMED", sep);
794 sep = ",";
795 }
796 if (statusmask == 0)
797 printf("%sNONE", sep);
798 printf(" ");
799}
800
801static void
802conntrack_dump_addr(const union nf_inet_addr *addr,
803 const union nf_inet_addr *mask,
804 unsigned int family, bool numeric)
805{
806 if (family == NFPROTO_IPV4) {
807 if (!numeric && addr->ip == 0) {
808 printf("anywhere ");
809 return;
810 }
811 if (numeric)
812 printf("%s ", xtables_ipaddr_to_numeric(&addr->in));
813 else
814 printf("%s ", xtables_ipaddr_to_anyname(&addr->in));
815 } else if (family == NFPROTO_IPV6) {
816 if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
817 addr->ip6[2] == 0 && addr->ip6[3] == 0) {
818 printf("anywhere ");
819 return;
820 }
821 if (numeric)
822 printf("%s ", xtables_ip6addr_to_numeric(&addr->in6));
823 else
824 printf("%s ", xtables_ip6addr_to_anyname(&addr->in6));
825 }
826}
827
828static void
829print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
830{
831 char buf[BUFSIZ];
832
833 if (inv)
834 printf("! ");
835
836 if (mask->s_addr == 0L && !numeric)
837 printf("%s ", "anywhere");
838 else {
839 if (numeric)
840 strcpy(buf, xtables_ipaddr_to_numeric(addr));
841 else
842 strcpy(buf, xtables_ipaddr_to_anyname(addr));
843 strcat(buf, xtables_ipmask_to_numeric(mask));
844 printf("%s ", buf);
845 }
846}
847
848static void
849matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
850{
851 struct xt_conntrack_info *sinfo = (void *)match->data;
852
853 if(sinfo->flags & XT_CONNTRACK_STATE) {
854 if (sinfo->invflags & XT_CONNTRACK_STATE)
855 printf("! ");
856 printf("%sctstate ", optpfx);
857 print_state(sinfo->statemask);
858 }
859
860 if(sinfo->flags & XT_CONNTRACK_PROTO) {
861 if (sinfo->invflags & XT_CONNTRACK_PROTO)
862 printf("! ");
863 printf("%sctproto ", optpfx);
864 printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
865 }
866
867 if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
868 if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
869 printf("! ");
870 printf("%sctorigsrc ", optpfx);
871
872 print_addr(
873 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
874 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
875 false,
876 numeric);
877 }
878
879 if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
880 if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
881 printf("! ");
882 printf("%sctorigdst ", optpfx);
883
884 print_addr(
885 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
886 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
887 false,
888 numeric);
889 }
890
891 if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
892 if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
893 printf("! ");
894 printf("%sctreplsrc ", optpfx);
895
896 print_addr(
897 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
898 &sinfo->sipmsk[IP_CT_DIR_REPLY],
899 false,
900 numeric);
901 }
902
903 if(sinfo->flags & XT_CONNTRACK_REPLDST) {
904 if (sinfo->invflags & XT_CONNTRACK_REPLDST)
905 printf("! ");
906 printf("%sctrepldst ", optpfx);
907
908 print_addr(
909 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
910 &sinfo->dipmsk[IP_CT_DIR_REPLY],
911 false,
912 numeric);
913 }
914
915 if(sinfo->flags & XT_CONNTRACK_STATUS) {
916 if (sinfo->invflags & XT_CONNTRACK_STATUS)
917 printf("! ");
918 printf("%sctstatus ", optpfx);
919 print_status(sinfo->statusmask);
920 }
921
922 if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
923 if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
924 printf("! ");
925 printf("%sctexpire ", optpfx);
926
927 if (sinfo->expires_max == sinfo->expires_min)
928 printf("%lu ", sinfo->expires_min);
929 else
930 printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
931 }
932
933 if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
934 if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
935 printf("%sctdir REPLY", optpfx);
936 else
937 printf("%sctdir ORIGINAL", optpfx);
938 }
939
940}
941
942static void
943conntrack_dump(const struct xt_conntrack_mtinfo2 *info, const char *prefix,
944 unsigned int family, bool numeric)
945{
946 if (info->match_flags & XT_CONNTRACK_STATE) {
947 if (info->invert_flags & XT_CONNTRACK_STATE)
948 printf("! ");
949 printf("%sctstate ", prefix);
950 print_state(info->state_mask);
951 }
952
953 if (info->match_flags & XT_CONNTRACK_PROTO) {
954 if (info->invert_flags & XT_CONNTRACK_PROTO)
955 printf("! ");
956 printf("%sctproto %u ", prefix, info->l4proto);
957 }
958
959 if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
960 if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
961 printf("! ");
962 printf("%sctorigsrc ", prefix);
963 conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
964 family, numeric);
965 }
966
967 if (info->match_flags & XT_CONNTRACK_ORIGDST) {
968 if (info->invert_flags & XT_CONNTRACK_ORIGDST)
969 printf("! ");
970 printf("%sctorigdst ", prefix);
971 conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
972 family, numeric);
973 }
974
975 if (info->match_flags & XT_CONNTRACK_REPLSRC) {
976 if (info->invert_flags & XT_CONNTRACK_REPLSRC)
977 printf("! ");
978 printf("%sctreplsrc ", prefix);
979 conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
980 family, numeric);
981 }
982
983 if (info->match_flags & XT_CONNTRACK_REPLDST) {
984 if (info->invert_flags & XT_CONNTRACK_REPLDST)
985 printf("! ");
986 printf("%sctrepldst ", prefix);
987 conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
988 family, numeric);
989 }
990
991 if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
992 if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
993 printf("! ");
994 printf("%sctorigsrcport %u ", prefix,
995 ntohs(info->origsrc_port));
996 }
997
998 if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
999 if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
1000 printf("! ");
1001 printf("%sctorigdstport %u ", prefix,
1002 ntohs(info->origdst_port));
1003 }
1004
1005 if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
1006 if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
1007 printf("! ");
1008 printf("%sctreplsrcport %u ", prefix,
1009 ntohs(info->replsrc_port));
1010 }
1011
1012 if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
1013 if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
1014 printf("! ");
1015 printf("%sctrepldstport %u ", prefix,
1016 ntohs(info->repldst_port));
1017 }
1018
1019 if (info->match_flags & XT_CONNTRACK_STATUS) {
1020 if (info->invert_flags & XT_CONNTRACK_STATUS)
1021 printf("! ");
1022 printf("%sctstatus ", prefix);
1023 print_status(info->status_mask);
1024 }
1025
1026 if (info->match_flags & XT_CONNTRACK_EXPIRES) {
1027 if (info->invert_flags & XT_CONNTRACK_EXPIRES)
1028 printf("! ");
1029 printf("%sctexpire ", prefix);
1030
1031 if (info->expires_max == info->expires_min)
1032 printf("%u ", (unsigned int)info->expires_min);
1033 else
1034 printf("%u:%u ", (unsigned int)info->expires_min,
1035 (unsigned int)info->expires_max);
1036 }
1037
1038 if (info->match_flags & XT_CONNTRACK_DIRECTION) {
1039 if (info->invert_flags & XT_CONNTRACK_DIRECTION)
1040 printf("%sctdir REPLY", prefix);
1041 else
1042 printf("%sctdir ORIGINAL", prefix);
1043 }
1044}
1045
1046static void conntrack_print(const void *ip, const struct xt_entry_match *match,
1047 int numeric)
1048{
1049 matchinfo_print(ip, match, numeric, "");
1050}
1051
1052static void
1053conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
1054 int numeric)
1055{
1056 const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1057 struct xt_conntrack_mtinfo2 up;
1058
1059 cinfo_transform(&up, info);
1060 conntrack_dump(&up, "", NFPROTO_IPV4, numeric);
1061}
1062
1063static void
1064conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
1065 int numeric)
1066{
1067 const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1068 struct xt_conntrack_mtinfo2 up;
1069
1070 cinfo_transform(&up, info);
1071 conntrack_dump(&up, "", NFPROTO_IPV6, numeric);
1072}
1073
1074static void
1075conntrack_mt_print(const void *ip, const struct xt_entry_match *match,
1076 int numeric)
1077{
1078 conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric);
1079}
1080
1081static void
1082conntrack_mt6_print(const void *ip, const struct xt_entry_match *match,
1083 int numeric)
1084{
1085 conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric);
1086}
1087
1088static void conntrack_save(const void *ip, const struct xt_entry_match *match)
1089{
1090 matchinfo_print(ip, match, 1, "--");
1091}
1092
1093static void conntrack_mt_save(const void *ip,
1094 const struct xt_entry_match *match)
1095{
1096 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true);
1097}
1098
1099static void conntrack_mt6_save(const void *ip,
1100 const struct xt_entry_match *match)
1101{
1102 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true);
1103}
1104
1105static void
1106conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
1107{
1108 const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1109 struct xt_conntrack_mtinfo2 up;
1110
1111 cinfo_transform(&up, info);
1112 conntrack_dump(&up, "--", NFPROTO_IPV4, true);
1113}
1114
1115static void
1116conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
1117{
1118 const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1119 struct xt_conntrack_mtinfo2 up;
1120
1121 cinfo_transform(&up, info);
1122 conntrack_dump(&up, "--", NFPROTO_IPV6, true);
1123}
1124
1125static struct xtables_match conntrack_mt_v0_reg = {
1126 .version = XTABLES_VERSION,
1127 .name = "conntrack",
1128 .revision = 0,
1129 .family = NFPROTO_IPV4,
1130 .size = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1131 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1132 .help = conntrack_mt_help,
1133 .parse = conntrack_parse,
1134 .final_check = conntrack_mt_check,
1135 .print = conntrack_print,
1136 .save = conntrack_save,
1137 .extra_opts = conntrack_mt_opts_v0,
1138};
1139
1140static struct xtables_match conntrack_mt_v1_reg = {
1141 .version = XTABLES_VERSION,
1142 .name = "conntrack",
1143 .revision = 1,
1144 .family = NFPROTO_IPV4,
1145 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1146 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1147 .help = conntrack_mt_help,
1148 .parse = conntrack1_mt4_parse,
1149 .final_check = conntrack_mt_check,
1150 .print = conntrack1_mt4_print,
1151 .save = conntrack1_mt4_save,
1152 .extra_opts = conntrack_mt_opts,
1153};
1154
1155static struct xtables_match conntrack_mt6_v1_reg = {
1156 .version = XTABLES_VERSION,
1157 .name = "conntrack",
1158 .revision = 1,
1159 .family = NFPROTO_IPV6,
1160 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1161 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1162 .help = conntrack_mt_help,
1163 .parse = conntrack1_mt6_parse,
1164 .final_check = conntrack_mt_check,
1165 .print = conntrack1_mt6_print,
1166 .save = conntrack1_mt6_save,
1167 .extra_opts = conntrack_mt_opts,
1168};
1169
1170static struct xtables_match conntrack_mt_v2_reg = {
1171 .version = XTABLES_VERSION,
1172 .name = "conntrack",
1173 .revision = 2,
1174 .family = NFPROTO_IPV4,
1175 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1176 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1177 .help = conntrack_mt_help,
1178 .parse = conntrack2_mt4_parse,
1179 .final_check = conntrack_mt_check,
1180 .print = conntrack_mt_print,
1181 .save = conntrack_mt_save,
1182 .extra_opts = conntrack_mt_opts,
1183};
1184
1185static struct xtables_match conntrack_mt6_v2_reg = {
1186 .version = XTABLES_VERSION,
1187 .name = "conntrack",
1188 .revision = 2,
1189 .family = NFPROTO_IPV6,
1190 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1191 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1192 .help = conntrack_mt_help,
1193 .parse = conntrack2_mt6_parse,
1194 .final_check = conntrack_mt_check,
1195 .print = conntrack_mt6_print,
1196 .save = conntrack_mt6_save,
1197 .extra_opts = conntrack_mt_opts,
1198};
1199
1200void _init(void)
1201{
1202 xtables_register_match(&conntrack_mt_v0_reg);
1203 xtables_register_match(&conntrack_mt_v1_reg);
1204 xtables_register_match(&conntrack_mt6_v1_reg);
1205 xtables_register_match(&conntrack_mt_v2_reg);
1206 xtables_register_match(&conntrack_mt6_v2_reg);
1207}