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);