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 #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' },
58 /* Initialize the match. */
60 init(struct ipt_entry_match *m, unsigned int *nfcache)
62 /* Can't cache this */
63 *nfcache |= NFC_UNKNOWN;
67 parse_state(const char *state, size_t strlen, struct ipt_conntrack_info *sinfo)
69 if (strncasecmp(state, "INVALID", strlen) == 0)
70 sinfo->statemask |= IPT_CONNTRACK_STATE_INVALID;
71 else if (strncasecmp(state, "NEW", strlen) == 0)
72 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_NEW);
73 else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
74 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
75 else if (strncasecmp(state, "RELATED", strlen) == 0)
76 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
77 else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
78 sinfo->statemask |= IPT_CONNTRACK_STATE_UNTRACKED;
79 else if (strncasecmp(state, "SNAT", strlen) == 0)
80 sinfo->statemask |= IPT_CONNTRACK_STATE_SNAT;
81 else if (strncasecmp(state, "DNAT", strlen) == 0)
82 sinfo->statemask |= IPT_CONNTRACK_STATE_DNAT;
89 parse_states(const char *arg, struct ipt_conntrack_info *sinfo)
93 while ((comma = strchr(arg, ',')) != NULL) {
94 if (comma == arg || !parse_state(arg, comma-arg, sinfo))
95 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
99 if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
100 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
104 parse_status(const char *status, size_t strlen, struct ipt_conntrack_info *sinfo)
106 if (strncasecmp(status, "NONE", strlen) == 0)
107 sinfo->statusmask |= 0;
108 else if (strncasecmp(status, "EXPECTED", strlen) == 0)
109 sinfo->statusmask |= IPS_EXPECTED;
110 else if (strncasecmp(status, "SEEN_REPLY", strlen) == 0)
111 sinfo->statusmask |= IPS_SEEN_REPLY;
112 else if (strncasecmp(status, "ASSURED", strlen) == 0)
113 sinfo->statusmask |= IPS_ASSURED;
115 else if (strncasecmp(status, "CONFIRMED", strlen) == 0)
116 sinfo->stausmask |= IPS_CONFIRMED;
124 parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
128 while ((comma = strchr(arg, ',')) != NULL) {
129 if (comma == arg || !parse_status(arg, comma-arg, sinfo))
130 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
134 if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
135 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
140 parse_expire(const char *s)
144 if (string_to_number(s, 0, 0xFFFFFFFF, &len) == -1)
145 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
150 /* If a single value is provided, min and max are both set to the value */
152 parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
158 if ((cp = strchr(buffer, ':')) == NULL)
159 sinfo->expires_min = sinfo->expires_max = parse_expire(buffer);
164 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
165 sinfo->expires_max = cp[0] ? parse_expire(cp) : 0xFFFFFFFF;
169 if (sinfo->expires_min > sinfo->expires_max)
170 exit_error(PARAMETER_PROBLEM,
171 "expire min. range value `%lu' greater than max. "
172 "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
176 /* Function which parses command options; returns true if it
179 parse(int c, char **argv, int invert, unsigned int *flags,
180 const struct ipt_entry *entry,
181 unsigned int *nfcache,
182 struct ipt_entry_match **match)
184 struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)(*match)->data;
185 char *protocol = NULL;
186 unsigned int naddrs = 0;
187 struct in_addr *addrs = NULL;
192 check_inverse(optarg, &invert, &optind, 0);
194 parse_states(argv[optind-1], sinfo);
196 sinfo->invflags |= IPT_CONNTRACK_STATE;
198 sinfo->flags |= IPT_CONNTRACK_STATE;
202 check_inverse(optarg, &invert, &optind, 0);
205 sinfo->invflags |= IPT_CONNTRACK_PROTO;
207 /* Canonicalize into lower case */
208 for (protocol = argv[optind-1]; *protocol; protocol++)
209 *protocol = tolower(*protocol);
211 protocol = argv[optind-1];
212 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol);
214 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
215 && (sinfo->invflags & IPT_INV_PROTO))
216 exit_error(PARAMETER_PROBLEM,
217 "rule would never match protocol");
219 sinfo->flags |= IPT_CONNTRACK_PROTO;
223 check_inverse(optarg, &invert, &optind, 9);
226 sinfo->invflags |= IPT_CONNTRACK_ORIGSRC;
228 parse_hostnetworkmask(argv[optind-1], &addrs,
229 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
232 exit_error(PARAMETER_PROBLEM,
233 "multiple IP addresses not allowed");
236 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
239 sinfo->flags |= IPT_CONNTRACK_ORIGSRC;
243 check_inverse(optarg, &invert, &optind, 0);
246 sinfo->invflags |= IPT_CONNTRACK_ORIGDST;
248 parse_hostnetworkmask(argv[optind-1], &addrs,
249 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
252 exit_error(PARAMETER_PROBLEM,
253 "multiple IP addresses not allowed");
256 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
259 sinfo->flags |= IPT_CONNTRACK_ORIGDST;
263 check_inverse(optarg, &invert, &optind, 0);
266 sinfo->invflags |= IPT_CONNTRACK_REPLSRC;
268 parse_hostnetworkmask(argv[optind-1], &addrs,
269 &sinfo->sipmsk[IP_CT_DIR_REPLY],
272 exit_error(PARAMETER_PROBLEM,
273 "multiple IP addresses not allowed");
276 sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
279 sinfo->flags |= IPT_CONNTRACK_REPLSRC;
283 check_inverse(optarg, &invert, &optind, 0);
286 sinfo->invflags |= IPT_CONNTRACK_REPLDST;
288 parse_hostnetworkmask(argv[optind-1], &addrs,
289 &sinfo->dipmsk[IP_CT_DIR_REPLY],
292 exit_error(PARAMETER_PROBLEM,
293 "multiple IP addresses not allowed");
296 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
299 sinfo->flags |= IPT_CONNTRACK_REPLDST;
303 check_inverse(optarg, &invert, &optind, 0);
305 parse_statuses(argv[optind-1], sinfo);
307 sinfo->invflags |= IPT_CONNTRACK_STATUS;
309 sinfo->flags |= IPT_CONNTRACK_STATUS;
313 check_inverse(optarg, &invert, &optind, 0);
315 parse_expires(argv[optind-1], sinfo);
317 sinfo->invflags |= IPT_CONNTRACK_EXPIRES;
319 sinfo->flags |= IPT_CONNTRACK_EXPIRES;
326 *flags = sinfo->flags;
331 final_check(unsigned int flags)
334 exit_error(PARAMETER_PROBLEM, "You must specify one or more options");
338 print_state(unsigned int statemask)
340 const char *sep = "";
342 if (statemask & IPT_CONNTRACK_STATE_INVALID) {
343 printf("%sINVALID", sep);
346 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
347 printf("%sNEW", sep);
350 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
351 printf("%sRELATED", sep);
354 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
355 printf("%sESTABLISHED", sep);
358 if (statemask & IPT_CONNTRACK_STATE_UNTRACKED) {
359 printf("%sUNTRACKED", sep);
362 if (statemask & IPT_CONNTRACK_STATE_SNAT) {
363 printf("%sSNAT", sep);
366 if (statemask & IPT_CONNTRACK_STATE_DNAT) {
367 printf("%sDNAT", sep);
374 print_status(unsigned int statusmask)
376 const char *sep = "";
378 if (statusmask & IPS_EXPECTED) {
379 printf("%sEXPECTED", sep);
382 if (statusmask & IPS_SEEN_REPLY) {
383 printf("%sSEEN_REPLY", sep);
386 if (statusmask & IPS_ASSURED) {
387 printf("%sASSURED", sep);
391 if (statusmask & IPS_CONFIRMED) {
392 printf("%sCONFIRMED", sep);
396 if (statusmask == 0) {
397 printf("%sNONE", sep);
404 print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
411 if (mask->s_addr == 0L && !numeric)
412 printf("%s ", "anywhere");
415 sprintf(buf, "%s", addr_to_dotted(addr));
417 sprintf(buf, "%s", addr_to_anyname(addr));
418 strcat(buf, mask_to_dotted(mask));
423 /* Saves the matchinfo in parsable form to stdout. */
425 matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric, const char *optpfx)
427 struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)match->data;
429 if(sinfo->flags & IPT_CONNTRACK_STATE) {
430 printf("%sctstate ", optpfx);
431 if (sinfo->invflags & IPT_CONNTRACK_STATE)
433 print_state(sinfo->statemask);
436 if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) {
437 printf("%sctorigsrc ", optpfx);
440 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
441 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
442 sinfo->invflags & IPT_CONNTRACK_ORIGSRC,
446 if(sinfo->flags & IPT_CONNTRACK_ORIGDST) {
447 printf("%sctorigdst ", optpfx);
450 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
451 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
452 sinfo->invflags & IPT_CONNTRACK_ORIGDST,
456 if(sinfo->flags & IPT_CONNTRACK_REPLSRC) {
457 printf("%sctreplsrc ", optpfx);
460 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
461 &sinfo->sipmsk[IP_CT_DIR_REPLY],
462 sinfo->invflags & IPT_CONNTRACK_REPLSRC,
466 if(sinfo->flags & IPT_CONNTRACK_REPLDST) {
467 printf("%sctrepldst ", optpfx);
470 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
471 &sinfo->dipmsk[IP_CT_DIR_REPLY],
472 sinfo->invflags & IPT_CONNTRACK_REPLDST,
476 if(sinfo->flags & IPT_CONNTRACK_STATUS) {
477 printf("%sctstatus ", optpfx);
478 if (sinfo->invflags & IPT_CONNTRACK_STATE)
480 print_status(sinfo->statusmask);
483 if(sinfo->flags & IPT_CONNTRACK_EXPIRES) {
484 printf("%sctexpire ", optpfx);
485 if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
488 if (sinfo->expires_max == sinfo->expires_min)
489 printf("%lu ", sinfo->expires_min);
491 printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
495 /* Prints out the matchinfo. */
497 print(const struct ipt_ip *ip,
498 const struct ipt_entry_match *match,
501 matchinfo_print(ip, match, numeric, "");
504 /* Saves the matchinfo in parsable form to stdout. */
505 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
507 matchinfo_print(ip, match, 0, "--");
511 struct iptables_match conntrack
515 IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
516 IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
528 register_match(&conntrack);