1 /* Shared library add-on to iptables for conntrack matching support.
2 * GPL (C) 2001 Marc Boucher (marc@mbsi.ca).
12 #include <linux/netfilter_ipv4/ip_conntrack.h>
13 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
14 /* For 64bit kernel / 32bit userspace */
15 #include "../include/linux/netfilter_ipv4/ipt_conntrack.h"
17 #ifndef IPT_CONNTRACK_STATE_UNTRACKED
18 #define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
21 /* Function which prints out usage message. */
26 "conntrack match v%s options:\n"
27 " [!] --ctstate [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT][,...]\n"
28 " State(s) to match\n"
29 " [!] --ctproto proto Protocol to match; by number or name, eg. `tcp'\n"
30 " --ctorigsrc [!] address[/mask]\n"
31 " Original source specification\n"
32 " --ctorigdst [!] address[/mask]\n"
33 " Original destination specification\n"
34 " --ctreplsrc [!] address[/mask]\n"
35 " Reply source specification\n"
36 " --ctrepldst [!] address[/mask]\n"
37 " Reply destination specification\n"
38 " [!] --ctstatus [NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED][,...]\n"
39 " Status(es) to match\n"
40 " [!] --ctexpire time[:time] Match remaining lifetime in seconds against\n"
41 " value or range of values (inclusive)\n"
42 "\n", IPTABLES_VERSION);
47 static struct option opts[] = {
48 { "ctstate", 1, 0, '1' },
49 { "ctproto", 1, 0, '2' },
50 { "ctorigsrc", 1, 0, '3' },
51 { "ctorigdst", 1, 0, '4' },
52 { "ctreplsrc", 1, 0, '5' },
53 { "ctrepldst", 1, 0, '6' },
54 { "ctstatus", 1, 0, '7' },
55 { "ctexpire", 1, 0, '8' },
60 parse_state(const char *state, size_t strlen, struct ipt_conntrack_info *sinfo)
62 if (strncasecmp(state, "INVALID", strlen) == 0)
63 sinfo->statemask |= IPT_CONNTRACK_STATE_INVALID;
64 else if (strncasecmp(state, "NEW", strlen) == 0)
65 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_NEW);
66 else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
67 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
68 else if (strncasecmp(state, "RELATED", strlen) == 0)
69 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
70 else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
71 sinfo->statemask |= IPT_CONNTRACK_STATE_UNTRACKED;
72 else if (strncasecmp(state, "SNAT", strlen) == 0)
73 sinfo->statemask |= IPT_CONNTRACK_STATE_SNAT;
74 else if (strncasecmp(state, "DNAT", strlen) == 0)
75 sinfo->statemask |= IPT_CONNTRACK_STATE_DNAT;
82 parse_states(const char *arg, struct ipt_conntrack_info *sinfo)
86 while ((comma = strchr(arg, ',')) != NULL) {
87 if (comma == arg || !parse_state(arg, comma-arg, sinfo))
88 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
92 if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
93 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
97 parse_status(const char *status, size_t strlen, struct ipt_conntrack_info *sinfo)
99 if (strncasecmp(status, "NONE", strlen) == 0)
100 sinfo->statusmask |= 0;
101 else if (strncasecmp(status, "EXPECTED", strlen) == 0)
102 sinfo->statusmask |= IPS_EXPECTED;
103 else if (strncasecmp(status, "SEEN_REPLY", strlen) == 0)
104 sinfo->statusmask |= IPS_SEEN_REPLY;
105 else if (strncasecmp(status, "ASSURED", strlen) == 0)
106 sinfo->statusmask |= IPS_ASSURED;
108 else if (strncasecmp(status, "CONFIRMED", strlen) == 0)
109 sinfo->stausmask |= IPS_CONFIRMED;
117 parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
121 while ((comma = strchr(arg, ',')) != NULL) {
122 if (comma == arg || !parse_status(arg, comma-arg, sinfo))
123 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
127 if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
128 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
131 #ifdef KERNEL_64_USERSPACE_32
132 static unsigned long long
133 parse_expire(const char *s)
135 unsigned long long len;
137 if (string_to_number_ll(s, 0, 0, &len) == -1)
138 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
144 parse_expire(const char *s)
148 if (string_to_number(s, 0, 0, &len) == -1)
149 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
155 /* If a single value is provided, min and max are both set to the value */
157 parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
163 if ((cp = strchr(buffer, ':')) == NULL)
164 sinfo->expires_min = sinfo->expires_max = parse_expire(buffer);
169 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
170 sinfo->expires_max = cp[0] ? parse_expire(cp) : -1;
174 if (sinfo->expires_min > sinfo->expires_max)
175 exit_error(PARAMETER_PROBLEM,
176 #ifdef KERNEL_64_USERSPACE_32
177 "expire min. range value `%llu' greater than max. "
178 "range value `%llu'", sinfo->expires_min, sinfo->expires_max);
180 "expire min. range value `%lu' greater than max. "
181 "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
185 /* Function which parses command options; returns true if it
188 parse(int c, char **argv, int invert, unsigned int *flags,
189 const struct ipt_entry *entry,
190 unsigned int *nfcache,
191 struct ipt_entry_match **match)
193 struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)(*match)->data;
194 char *protocol = NULL;
195 unsigned int naddrs = 0;
196 struct in_addr *addrs = NULL;
201 check_inverse(optarg, &invert, &optind, 0);
203 parse_states(argv[optind-1], sinfo);
205 sinfo->invflags |= IPT_CONNTRACK_STATE;
207 sinfo->flags |= IPT_CONNTRACK_STATE;
211 check_inverse(optarg, &invert, &optind, 0);
214 sinfo->invflags |= IPT_CONNTRACK_PROTO;
216 /* Canonicalize into lower case */
217 for (protocol = argv[optind-1]; *protocol; protocol++)
218 *protocol = tolower(*protocol);
220 protocol = argv[optind-1];
221 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol);
223 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
224 && (sinfo->invflags & IPT_INV_PROTO))
225 exit_error(PARAMETER_PROBLEM,
226 "rule would never match protocol");
228 sinfo->flags |= IPT_CONNTRACK_PROTO;
232 check_inverse(optarg, &invert, &optind, 9);
235 sinfo->invflags |= IPT_CONNTRACK_ORIGSRC;
237 parse_hostnetworkmask(argv[optind-1], &addrs,
238 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
241 exit_error(PARAMETER_PROBLEM,
242 "multiple IP addresses not allowed");
245 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
248 sinfo->flags |= IPT_CONNTRACK_ORIGSRC;
252 check_inverse(optarg, &invert, &optind, 0);
255 sinfo->invflags |= IPT_CONNTRACK_ORIGDST;
257 parse_hostnetworkmask(argv[optind-1], &addrs,
258 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
261 exit_error(PARAMETER_PROBLEM,
262 "multiple IP addresses not allowed");
265 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
268 sinfo->flags |= IPT_CONNTRACK_ORIGDST;
272 check_inverse(optarg, &invert, &optind, 0);
275 sinfo->invflags |= IPT_CONNTRACK_REPLSRC;
277 parse_hostnetworkmask(argv[optind-1], &addrs,
278 &sinfo->sipmsk[IP_CT_DIR_REPLY],
281 exit_error(PARAMETER_PROBLEM,
282 "multiple IP addresses not allowed");
285 sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
288 sinfo->flags |= IPT_CONNTRACK_REPLSRC;
292 check_inverse(optarg, &invert, &optind, 0);
295 sinfo->invflags |= IPT_CONNTRACK_REPLDST;
297 parse_hostnetworkmask(argv[optind-1], &addrs,
298 &sinfo->dipmsk[IP_CT_DIR_REPLY],
301 exit_error(PARAMETER_PROBLEM,
302 "multiple IP addresses not allowed");
305 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
308 sinfo->flags |= IPT_CONNTRACK_REPLDST;
312 check_inverse(optarg, &invert, &optind, 0);
314 parse_statuses(argv[optind-1], sinfo);
316 sinfo->invflags |= IPT_CONNTRACK_STATUS;
318 sinfo->flags |= IPT_CONNTRACK_STATUS;
322 check_inverse(optarg, &invert, &optind, 0);
324 parse_expires(argv[optind-1], sinfo);
326 sinfo->invflags |= IPT_CONNTRACK_EXPIRES;
328 sinfo->flags |= IPT_CONNTRACK_EXPIRES;
335 *flags = sinfo->flags;
340 final_check(unsigned int flags)
343 exit_error(PARAMETER_PROBLEM, "You must specify one or more options");
347 print_state(unsigned int statemask)
349 const char *sep = "";
351 if (statemask & IPT_CONNTRACK_STATE_INVALID) {
352 printf("%sINVALID", sep);
355 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
356 printf("%sNEW", sep);
359 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
360 printf("%sRELATED", sep);
363 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
364 printf("%sESTABLISHED", sep);
367 if (statemask & IPT_CONNTRACK_STATE_UNTRACKED) {
368 printf("%sUNTRACKED", sep);
371 if (statemask & IPT_CONNTRACK_STATE_SNAT) {
372 printf("%sSNAT", sep);
375 if (statemask & IPT_CONNTRACK_STATE_DNAT) {
376 printf("%sDNAT", sep);
383 print_status(unsigned int statusmask)
385 const char *sep = "";
387 if (statusmask & IPS_EXPECTED) {
388 printf("%sEXPECTED", sep);
391 if (statusmask & IPS_SEEN_REPLY) {
392 printf("%sSEEN_REPLY", sep);
395 if (statusmask & IPS_ASSURED) {
396 printf("%sASSURED", sep);
400 if (statusmask & IPS_CONFIRMED) {
401 printf("%sCONFIRMED", sep);
405 if (statusmask == 0) {
406 printf("%sNONE", sep);
413 print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
420 if (mask->s_addr == 0L && !numeric)
421 printf("%s ", "anywhere");
424 sprintf(buf, "%s", addr_to_dotted(addr));
426 sprintf(buf, "%s", addr_to_anyname(addr));
427 strcat(buf, mask_to_dotted(mask));
432 /* Saves the matchinfo in parsable form to stdout. */
434 matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric, const char *optpfx)
436 struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)match->data;
438 if(sinfo->flags & IPT_CONNTRACK_STATE) {
439 printf("%sctstate ", optpfx);
440 if (sinfo->invflags & IPT_CONNTRACK_STATE)
442 print_state(sinfo->statemask);
445 if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) {
446 printf("%sctorigsrc ", optpfx);
449 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
450 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
451 sinfo->invflags & IPT_CONNTRACK_ORIGSRC,
455 if(sinfo->flags & IPT_CONNTRACK_ORIGDST) {
456 printf("%sctorigdst ", optpfx);
459 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
460 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
461 sinfo->invflags & IPT_CONNTRACK_ORIGDST,
465 if(sinfo->flags & IPT_CONNTRACK_REPLSRC) {
466 printf("%sctreplsrc ", optpfx);
469 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
470 &sinfo->sipmsk[IP_CT_DIR_REPLY],
471 sinfo->invflags & IPT_CONNTRACK_REPLSRC,
475 if(sinfo->flags & IPT_CONNTRACK_REPLDST) {
476 printf("%sctrepldst ", optpfx);
479 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
480 &sinfo->dipmsk[IP_CT_DIR_REPLY],
481 sinfo->invflags & IPT_CONNTRACK_REPLDST,
485 if(sinfo->flags & IPT_CONNTRACK_STATUS) {
486 printf("%sctstatus ", optpfx);
487 if (sinfo->invflags & IPT_CONNTRACK_STATUS)
489 print_status(sinfo->statusmask);
492 if(sinfo->flags & IPT_CONNTRACK_EXPIRES) {
493 printf("%sctexpire ", optpfx);
494 if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
497 #ifdef KERNEL_64_USERSPACE_32
498 if (sinfo->expires_max == sinfo->expires_min)
499 printf("%llu ", sinfo->expires_min);
501 printf("%llu:%llu ", sinfo->expires_min, sinfo->expires_max);
503 if (sinfo->expires_max == sinfo->expires_min)
504 printf("%lu ", sinfo->expires_min);
506 printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
511 /* Prints out the matchinfo. */
513 print(const struct ipt_ip *ip,
514 const struct ipt_entry_match *match,
517 matchinfo_print(ip, match, numeric, "");
520 /* Saves the matchinfo in parsable form to stdout. */
521 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
523 matchinfo_print(ip, match, 1, "--");
526 static struct iptables_match conntrack = {
529 .version = IPTABLES_VERSION,
530 .size = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
531 .userspacesize = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
534 .final_check = &final_check,
542 register_match(&conntrack);