1 /* Shared library add-on to iptables for conntrack matching support.
2 * GPL (C) 2001 Marc Boucher (marc@mbsi.ca).
12 #include <linux/netfilter/nf_conntrack_common.h>
13 /* For 64bit kernel / 32bit userspace */
14 #include "../include/linux/netfilter_ipv4/ipt_conntrack.h"
16 #ifndef IPT_CONNTRACK_STATE_UNTRACKED
17 #define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
20 /* Function which prints out usage message. */
25 "conntrack match v%s options:\n"
26 " [!] --ctstate [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT][,...]\n"
27 " State(s) to match\n"
28 " [!] --ctproto proto Protocol to match; by number or name, eg. `tcp'\n"
29 " --ctorigsrc [!] address[/mask]\n"
30 " Original source specification\n"
31 " --ctorigdst [!] address[/mask]\n"
32 " Original destination specification\n"
33 " --ctreplsrc [!] address[/mask]\n"
34 " Reply source specification\n"
35 " --ctrepldst [!] address[/mask]\n"
36 " Reply destination specification\n"
37 " [!] --ctstatus [NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED][,...]\n"
38 " Status(es) to match\n"
39 " [!] --ctexpire time[:time] Match remaining lifetime in seconds against\n"
40 " value or range of values (inclusive)\n"
41 "\n", IPTABLES_VERSION
);
46 static struct option opts
[] = {
47 { "ctstate", 1, 0, '1' },
48 { "ctproto", 1, 0, '2' },
49 { "ctorigsrc", 1, 0, '3' },
50 { "ctorigdst", 1, 0, '4' },
51 { "ctreplsrc", 1, 0, '5' },
52 { "ctrepldst", 1, 0, '6' },
53 { "ctstatus", 1, 0, '7' },
54 { "ctexpire", 1, 0, '8' },
59 parse_state(const char *state
, size_t strlen
, struct ipt_conntrack_info
*sinfo
)
61 if (strncasecmp(state
, "INVALID", strlen
) == 0)
62 sinfo
->statemask
|= IPT_CONNTRACK_STATE_INVALID
;
63 else if (strncasecmp(state
, "NEW", strlen
) == 0)
64 sinfo
->statemask
|= IPT_CONNTRACK_STATE_BIT(IP_CT_NEW
);
65 else if (strncasecmp(state
, "ESTABLISHED", strlen
) == 0)
66 sinfo
->statemask
|= IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED
);
67 else if (strncasecmp(state
, "RELATED", strlen
) == 0)
68 sinfo
->statemask
|= IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED
);
69 else if (strncasecmp(state
, "UNTRACKED", strlen
) == 0)
70 sinfo
->statemask
|= IPT_CONNTRACK_STATE_UNTRACKED
;
71 else if (strncasecmp(state
, "SNAT", strlen
) == 0)
72 sinfo
->statemask
|= IPT_CONNTRACK_STATE_SNAT
;
73 else if (strncasecmp(state
, "DNAT", strlen
) == 0)
74 sinfo
->statemask
|= IPT_CONNTRACK_STATE_DNAT
;
81 parse_states(const char *arg
, struct ipt_conntrack_info
*sinfo
)
85 while ((comma
= strchr(arg
, ',')) != NULL
) {
86 if (comma
== arg
|| !parse_state(arg
, comma
-arg
, sinfo
))
87 exit_error(PARAMETER_PROBLEM
, "Bad ctstate `%s'", arg
);
91 if (strlen(arg
) == 0 || !parse_state(arg
, strlen(arg
), sinfo
))
92 exit_error(PARAMETER_PROBLEM
, "Bad ctstate `%s'", arg
);
96 parse_status(const char *status
, size_t strlen
, struct ipt_conntrack_info
*sinfo
)
98 if (strncasecmp(status
, "NONE", strlen
) == 0)
99 sinfo
->statusmask
|= 0;
100 else if (strncasecmp(status
, "EXPECTED", strlen
) == 0)
101 sinfo
->statusmask
|= IPS_EXPECTED
;
102 else if (strncasecmp(status
, "SEEN_REPLY", strlen
) == 0)
103 sinfo
->statusmask
|= IPS_SEEN_REPLY
;
104 else if (strncasecmp(status
, "ASSURED", strlen
) == 0)
105 sinfo
->statusmask
|= IPS_ASSURED
;
107 else if (strncasecmp(status
, "CONFIRMED", strlen
) == 0)
108 sinfo
->stausmask
|= IPS_CONFIRMED
;
116 parse_statuses(const char *arg
, struct ipt_conntrack_info
*sinfo
)
120 while ((comma
= strchr(arg
, ',')) != NULL
) {
121 if (comma
== arg
|| !parse_status(arg
, comma
-arg
, sinfo
))
122 exit_error(PARAMETER_PROBLEM
, "Bad ctstatus `%s'", arg
);
126 if (strlen(arg
) == 0 || !parse_status(arg
, strlen(arg
), sinfo
))
127 exit_error(PARAMETER_PROBLEM
, "Bad ctstatus `%s'", arg
);
130 #ifdef KERNEL_64_USERSPACE_32
131 static unsigned long long
132 parse_expire(const char *s
)
134 unsigned long long len
;
136 if (string_to_number_ll(s
, 0, 0, &len
) == -1)
137 exit_error(PARAMETER_PROBLEM
, "expire value invalid: `%s'\n", s
);
143 parse_expire(const char *s
)
147 if (string_to_number(s
, 0, 0, &len
) == -1)
148 exit_error(PARAMETER_PROBLEM
, "expire value invalid: `%s'\n", s
);
154 /* If a single value is provided, min and max are both set to the value */
156 parse_expires(const char *s
, struct ipt_conntrack_info
*sinfo
)
162 if ((cp
= strchr(buffer
, ':')) == NULL
)
163 sinfo
->expires_min
= sinfo
->expires_max
= parse_expire(buffer
);
168 sinfo
->expires_min
= buffer
[0] ? parse_expire(buffer
) : 0;
169 sinfo
->expires_max
= cp
[0] ? parse_expire(cp
) : -1;
173 if (sinfo
->expires_min
> sinfo
->expires_max
)
174 exit_error(PARAMETER_PROBLEM
,
175 #ifdef KERNEL_64_USERSPACE_32
176 "expire min. range value `%llu' greater than max. "
177 "range value `%llu'", sinfo
->expires_min
, sinfo
->expires_max
);
179 "expire min. range value `%lu' greater than max. "
180 "range value `%lu'", sinfo
->expires_min
, sinfo
->expires_max
);
184 /* Function which parses command options; returns true if it
187 parse(int c
, char **argv
, int invert
, unsigned int *flags
,
188 const struct ipt_entry
*entry
,
189 unsigned int *nfcache
,
190 struct ipt_entry_match
**match
)
192 struct ipt_conntrack_info
*sinfo
= (struct ipt_conntrack_info
*)(*match
)->data
;
193 char *protocol
= NULL
;
194 unsigned int naddrs
= 0;
195 struct in_addr
*addrs
= NULL
;
200 check_inverse(optarg
, &invert
, &optind
, 0);
202 parse_states(argv
[optind
-1], sinfo
);
204 sinfo
->invflags
|= IPT_CONNTRACK_STATE
;
206 sinfo
->flags
|= IPT_CONNTRACK_STATE
;
210 check_inverse(optarg
, &invert
, &optind
, 0);
213 sinfo
->invflags
|= IPT_CONNTRACK_PROTO
;
215 /* Canonicalize into lower case */
216 for (protocol
= argv
[optind
-1]; *protocol
; protocol
++)
217 *protocol
= tolower(*protocol
);
219 protocol
= argv
[optind
-1];
220 sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].dst
.protonum
= parse_protocol(protocol
);
222 if (sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].dst
.protonum
== 0
223 && (sinfo
->invflags
& IPT_INV_PROTO
))
224 exit_error(PARAMETER_PROBLEM
,
225 "rule would never match protocol");
227 sinfo
->flags
|= IPT_CONNTRACK_PROTO
;
231 check_inverse(optarg
, &invert
, &optind
, 9);
234 sinfo
->invflags
|= IPT_CONNTRACK_ORIGSRC
;
236 parse_hostnetworkmask(argv
[optind
-1], &addrs
,
237 &sinfo
->sipmsk
[IP_CT_DIR_ORIGINAL
],
240 exit_error(PARAMETER_PROBLEM
,
241 "multiple IP addresses not allowed");
244 sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].src
.ip
= addrs
[0].s_addr
;
247 sinfo
->flags
|= IPT_CONNTRACK_ORIGSRC
;
251 check_inverse(optarg
, &invert
, &optind
, 0);
254 sinfo
->invflags
|= IPT_CONNTRACK_ORIGDST
;
256 parse_hostnetworkmask(argv
[optind
-1], &addrs
,
257 &sinfo
->dipmsk
[IP_CT_DIR_ORIGINAL
],
260 exit_error(PARAMETER_PROBLEM
,
261 "multiple IP addresses not allowed");
264 sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].dst
.ip
= addrs
[0].s_addr
;
267 sinfo
->flags
|= IPT_CONNTRACK_ORIGDST
;
271 check_inverse(optarg
, &invert
, &optind
, 0);
274 sinfo
->invflags
|= IPT_CONNTRACK_REPLSRC
;
276 parse_hostnetworkmask(argv
[optind
-1], &addrs
,
277 &sinfo
->sipmsk
[IP_CT_DIR_REPLY
],
280 exit_error(PARAMETER_PROBLEM
,
281 "multiple IP addresses not allowed");
284 sinfo
->tuple
[IP_CT_DIR_REPLY
].src
.ip
= addrs
[0].s_addr
;
287 sinfo
->flags
|= IPT_CONNTRACK_REPLSRC
;
291 check_inverse(optarg
, &invert
, &optind
, 0);
294 sinfo
->invflags
|= IPT_CONNTRACK_REPLDST
;
296 parse_hostnetworkmask(argv
[optind
-1], &addrs
,
297 &sinfo
->dipmsk
[IP_CT_DIR_REPLY
],
300 exit_error(PARAMETER_PROBLEM
,
301 "multiple IP addresses not allowed");
304 sinfo
->tuple
[IP_CT_DIR_REPLY
].dst
.ip
= addrs
[0].s_addr
;
307 sinfo
->flags
|= IPT_CONNTRACK_REPLDST
;
311 check_inverse(optarg
, &invert
, &optind
, 0);
313 parse_statuses(argv
[optind
-1], sinfo
);
315 sinfo
->invflags
|= IPT_CONNTRACK_STATUS
;
317 sinfo
->flags
|= IPT_CONNTRACK_STATUS
;
321 check_inverse(optarg
, &invert
, &optind
, 0);
323 parse_expires(argv
[optind
-1], sinfo
);
325 sinfo
->invflags
|= IPT_CONNTRACK_EXPIRES
;
327 sinfo
->flags
|= IPT_CONNTRACK_EXPIRES
;
334 *flags
= sinfo
->flags
;
339 final_check(unsigned int flags
)
342 exit_error(PARAMETER_PROBLEM
, "You must specify one or more options");
346 print_state(unsigned int statemask
)
348 const char *sep
= "";
350 if (statemask
& IPT_CONNTRACK_STATE_INVALID
) {
351 printf("%sINVALID", sep
);
354 if (statemask
& IPT_CONNTRACK_STATE_BIT(IP_CT_NEW
)) {
355 printf("%sNEW", sep
);
358 if (statemask
& IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED
)) {
359 printf("%sRELATED", sep
);
362 if (statemask
& IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED
)) {
363 printf("%sESTABLISHED", sep
);
366 if (statemask
& IPT_CONNTRACK_STATE_UNTRACKED
) {
367 printf("%sUNTRACKED", sep
);
370 if (statemask
& IPT_CONNTRACK_STATE_SNAT
) {
371 printf("%sSNAT", sep
);
374 if (statemask
& IPT_CONNTRACK_STATE_DNAT
) {
375 printf("%sDNAT", sep
);
382 print_status(unsigned int statusmask
)
384 const char *sep
= "";
386 if (statusmask
& IPS_EXPECTED
) {
387 printf("%sEXPECTED", sep
);
390 if (statusmask
& IPS_SEEN_REPLY
) {
391 printf("%sSEEN_REPLY", sep
);
394 if (statusmask
& IPS_ASSURED
) {
395 printf("%sASSURED", sep
);
399 if (statusmask
& IPS_CONFIRMED
) {
400 printf("%sCONFIRMED", sep
);
404 if (statusmask
== 0) {
405 printf("%sNONE", sep
);
412 print_addr(struct in_addr
*addr
, struct in_addr
*mask
, int inv
, int numeric
)
419 if (mask
->s_addr
== 0L && !numeric
)
420 printf("%s ", "anywhere");
423 sprintf(buf
, "%s", addr_to_dotted(addr
));
425 sprintf(buf
, "%s", addr_to_anyname(addr
));
426 strcat(buf
, mask_to_dotted(mask
));
431 /* Saves the matchinfo in parsable form to stdout. */
433 matchinfo_print(const struct ipt_ip
*ip
, const struct ipt_entry_match
*match
, int numeric
, const char *optpfx
)
435 struct ipt_conntrack_info
*sinfo
= (struct ipt_conntrack_info
*)match
->data
;
437 if(sinfo
->flags
& IPT_CONNTRACK_STATE
) {
438 printf("%sctstate ", optpfx
);
439 if (sinfo
->invflags
& IPT_CONNTRACK_STATE
)
441 print_state(sinfo
->statemask
);
444 if(sinfo
->flags
& IPT_CONNTRACK_PROTO
) {
445 printf("%sctproto ", optpfx
);
446 if (sinfo
->invflags
& IPT_CONNTRACK_PROTO
)
448 printf("%u ", sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].dst
.protonum
);
451 if(sinfo
->flags
& IPT_CONNTRACK_ORIGSRC
) {
452 printf("%sctorigsrc ", optpfx
);
455 (struct in_addr
*)&sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].src
.ip
,
456 &sinfo
->sipmsk
[IP_CT_DIR_ORIGINAL
],
457 sinfo
->invflags
& IPT_CONNTRACK_ORIGSRC
,
461 if(sinfo
->flags
& IPT_CONNTRACK_ORIGDST
) {
462 printf("%sctorigdst ", optpfx
);
465 (struct in_addr
*)&sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].dst
.ip
,
466 &sinfo
->dipmsk
[IP_CT_DIR_ORIGINAL
],
467 sinfo
->invflags
& IPT_CONNTRACK_ORIGDST
,
471 if(sinfo
->flags
& IPT_CONNTRACK_REPLSRC
) {
472 printf("%sctreplsrc ", optpfx
);
475 (struct in_addr
*)&sinfo
->tuple
[IP_CT_DIR_REPLY
].src
.ip
,
476 &sinfo
->sipmsk
[IP_CT_DIR_REPLY
],
477 sinfo
->invflags
& IPT_CONNTRACK_REPLSRC
,
481 if(sinfo
->flags
& IPT_CONNTRACK_REPLDST
) {
482 printf("%sctrepldst ", optpfx
);
485 (struct in_addr
*)&sinfo
->tuple
[IP_CT_DIR_REPLY
].dst
.ip
,
486 &sinfo
->dipmsk
[IP_CT_DIR_REPLY
],
487 sinfo
->invflags
& IPT_CONNTRACK_REPLDST
,
491 if(sinfo
->flags
& IPT_CONNTRACK_STATUS
) {
492 printf("%sctstatus ", optpfx
);
493 if (sinfo
->invflags
& IPT_CONNTRACK_STATUS
)
495 print_status(sinfo
->statusmask
);
498 if(sinfo
->flags
& IPT_CONNTRACK_EXPIRES
) {
499 printf("%sctexpire ", optpfx
);
500 if (sinfo
->invflags
& IPT_CONNTRACK_EXPIRES
)
503 #ifdef KERNEL_64_USERSPACE_32
504 if (sinfo
->expires_max
== sinfo
->expires_min
)
505 printf("%llu ", sinfo
->expires_min
);
507 printf("%llu:%llu ", sinfo
->expires_min
, sinfo
->expires_max
);
509 if (sinfo
->expires_max
== sinfo
->expires_min
)
510 printf("%lu ", sinfo
->expires_min
);
512 printf("%lu:%lu ", sinfo
->expires_min
, sinfo
->expires_max
);
517 /* Prints out the matchinfo. */
519 print(const struct ipt_ip
*ip
,
520 const struct ipt_entry_match
*match
,
523 matchinfo_print(ip
, match
, numeric
, "");
526 /* Saves the matchinfo in parsable form to stdout. */
527 static void save(const struct ipt_ip
*ip
, const struct ipt_entry_match
*match
)
529 matchinfo_print(ip
, match
, 1, "--");
532 static struct iptables_match conntrack
= {
535 .version
= IPTABLES_VERSION
,
536 .size
= IPT_ALIGN(sizeof(struct ipt_conntrack_info
)),
537 .userspacesize
= IPT_ALIGN(sizeof(struct ipt_conntrack_info
)),
540 .final_check
= &final_check
,
548 register_match(&conntrack
);