Clean and tiddy-up files.
[tomato.git] / release / src / router / iptables / extensions / libipt_conntrack.c
blob2b62a8cd67ce94c0f7ce68ab5a013e671cb6d5cb
1 /* Shared library add-on to iptables for conntrack matching support.
2 * GPL (C) 2001 Marc Boucher (marc@mbsi.ca).
3 */
5 #include <stdio.h>
6 #include <netdb.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <getopt.h>
10 #include <ctype.h>
11 #include <iptables.h>
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))
18 #endif
20 /* Function which prints out usage message. */
21 static void
22 help(void)
24 printf(
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' },
55 {0}
58 static int
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;
75 else
76 return 0;
77 return 1;
80 static void
81 parse_states(const char *arg, struct ipt_conntrack_info *sinfo)
83 const char *comma;
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);
88 arg = comma+1;
91 if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
92 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
95 static int
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;
106 #ifdef IPS_CONFIRMED
107 else if (strncasecmp(status, "CONFIRMED", strlen) == 0)
108 sinfo->stausmask |= IPS_CONFIRMED;
109 #endif
110 else
111 return 0;
112 return 1;
115 static void
116 parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
118 const char *comma;
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);
123 arg = comma+1;
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);
138 else
139 return len;
141 #else
142 static unsigned long
143 parse_expire(const char *s)
145 unsigned int len;
147 if (string_to_number(s, 0, 0, &len) == -1)
148 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
149 else
150 return len;
152 #endif
154 /* If a single value is provided, min and max are both set to the value */
155 static void
156 parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
158 char *buffer;
159 char *cp;
161 buffer = strdup(s);
162 if ((cp = strchr(buffer, ':')) == NULL)
163 sinfo->expires_min = sinfo->expires_max = parse_expire(buffer);
164 else {
165 *cp = '\0';
166 cp++;
168 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
169 sinfo->expires_max = cp[0] ? parse_expire(cp) : -1;
171 free(buffer);
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);
178 #else
179 "expire min. range value `%lu' greater than max. "
180 "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
181 #endif
184 /* Function which parses command options; returns true if it
185 ate an option */
186 static int
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;
198 switch (c) {
199 case '1':
200 check_inverse(optarg, &invert, &optind, 0);
202 parse_states(argv[optind-1], sinfo);
203 if (invert) {
204 sinfo->invflags |= IPT_CONNTRACK_STATE;
206 sinfo->flags |= IPT_CONNTRACK_STATE;
207 break;
209 case '2':
210 check_inverse(optarg, &invert, &optind, 0);
212 if(invert)
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;
228 break;
230 case '3':
231 check_inverse(optarg, &invert, &optind, 9);
233 if (invert)
234 sinfo->invflags |= IPT_CONNTRACK_ORIGSRC;
236 parse_hostnetworkmask(argv[optind-1], &addrs,
237 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
238 &naddrs);
239 if(naddrs > 1)
240 exit_error(PARAMETER_PROBLEM,
241 "multiple IP addresses not allowed");
243 if(naddrs == 1) {
244 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
247 sinfo->flags |= IPT_CONNTRACK_ORIGSRC;
248 break;
250 case '4':
251 check_inverse(optarg, &invert, &optind, 0);
253 if (invert)
254 sinfo->invflags |= IPT_CONNTRACK_ORIGDST;
256 parse_hostnetworkmask(argv[optind-1], &addrs,
257 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
258 &naddrs);
259 if(naddrs > 1)
260 exit_error(PARAMETER_PROBLEM,
261 "multiple IP addresses not allowed");
263 if(naddrs == 1) {
264 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
267 sinfo->flags |= IPT_CONNTRACK_ORIGDST;
268 break;
270 case '5':
271 check_inverse(optarg, &invert, &optind, 0);
273 if (invert)
274 sinfo->invflags |= IPT_CONNTRACK_REPLSRC;
276 parse_hostnetworkmask(argv[optind-1], &addrs,
277 &sinfo->sipmsk[IP_CT_DIR_REPLY],
278 &naddrs);
279 if(naddrs > 1)
280 exit_error(PARAMETER_PROBLEM,
281 "multiple IP addresses not allowed");
283 if(naddrs == 1) {
284 sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
287 sinfo->flags |= IPT_CONNTRACK_REPLSRC;
288 break;
290 case '6':
291 check_inverse(optarg, &invert, &optind, 0);
293 if (invert)
294 sinfo->invflags |= IPT_CONNTRACK_REPLDST;
296 parse_hostnetworkmask(argv[optind-1], &addrs,
297 &sinfo->dipmsk[IP_CT_DIR_REPLY],
298 &naddrs);
299 if(naddrs > 1)
300 exit_error(PARAMETER_PROBLEM,
301 "multiple IP addresses not allowed");
303 if(naddrs == 1) {
304 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
307 sinfo->flags |= IPT_CONNTRACK_REPLDST;
308 break;
310 case '7':
311 check_inverse(optarg, &invert, &optind, 0);
313 parse_statuses(argv[optind-1], sinfo);
314 if (invert) {
315 sinfo->invflags |= IPT_CONNTRACK_STATUS;
317 sinfo->flags |= IPT_CONNTRACK_STATUS;
318 break;
320 case '8':
321 check_inverse(optarg, &invert, &optind, 0);
323 parse_expires(argv[optind-1], sinfo);
324 if (invert) {
325 sinfo->invflags |= IPT_CONNTRACK_EXPIRES;
327 sinfo->flags |= IPT_CONNTRACK_EXPIRES;
328 break;
330 default:
331 return 0;
334 *flags = sinfo->flags;
335 return 1;
338 static void
339 final_check(unsigned int flags)
341 if (!flags)
342 exit_error(PARAMETER_PROBLEM, "You must specify one or more options");
345 static void
346 print_state(unsigned int statemask)
348 const char *sep = "";
350 if (statemask & IPT_CONNTRACK_STATE_INVALID) {
351 printf("%sINVALID", sep);
352 sep = ",";
354 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
355 printf("%sNEW", sep);
356 sep = ",";
358 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
359 printf("%sRELATED", sep);
360 sep = ",";
362 if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
363 printf("%sESTABLISHED", sep);
364 sep = ",";
366 if (statemask & IPT_CONNTRACK_STATE_UNTRACKED) {
367 printf("%sUNTRACKED", sep);
368 sep = ",";
370 if (statemask & IPT_CONNTRACK_STATE_SNAT) {
371 printf("%sSNAT", sep);
372 sep = ",";
374 if (statemask & IPT_CONNTRACK_STATE_DNAT) {
375 printf("%sDNAT", sep);
376 sep = ",";
378 printf(" ");
381 static void
382 print_status(unsigned int statusmask)
384 const char *sep = "";
386 if (statusmask & IPS_EXPECTED) {
387 printf("%sEXPECTED", sep);
388 sep = ",";
390 if (statusmask & IPS_SEEN_REPLY) {
391 printf("%sSEEN_REPLY", sep);
392 sep = ",";
394 if (statusmask & IPS_ASSURED) {
395 printf("%sASSURED", sep);
396 sep = ",";
398 #ifdef IPS_CONFIRMED
399 if (statusmask & IPS_CONFIRMED) {
400 printf("%sCONFIRMED", sep);
401 sep =",";
403 #endif
404 if (statusmask == 0) {
405 printf("%sNONE", sep);
406 sep = ",";
408 printf(" ");
411 static void
412 print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
414 char buf[BUFSIZ];
416 if (inv)
417 printf("! ");
419 if (mask->s_addr == 0L && !numeric)
420 printf("%s ", "anywhere");
421 else {
422 if (numeric)
423 sprintf(buf, "%s", addr_to_dotted(addr));
424 else
425 sprintf(buf, "%s", addr_to_anyname(addr));
426 strcat(buf, mask_to_dotted(mask));
427 printf("%s ", buf);
431 /* Saves the matchinfo in parsable form to stdout. */
432 static void
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)
440 printf("! ");
441 print_state(sinfo->statemask);
444 if(sinfo->flags & IPT_CONNTRACK_PROTO) {
445 printf("%sctproto ", optpfx);
446 if (sinfo->invflags & IPT_CONNTRACK_PROTO)
447 printf("! ");
448 printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
451 if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) {
452 printf("%sctorigsrc ", optpfx);
454 print_addr(
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,
458 numeric);
461 if(sinfo->flags & IPT_CONNTRACK_ORIGDST) {
462 printf("%sctorigdst ", optpfx);
464 print_addr(
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,
468 numeric);
471 if(sinfo->flags & IPT_CONNTRACK_REPLSRC) {
472 printf("%sctreplsrc ", optpfx);
474 print_addr(
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,
478 numeric);
481 if(sinfo->flags & IPT_CONNTRACK_REPLDST) {
482 printf("%sctrepldst ", optpfx);
484 print_addr(
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,
488 numeric);
491 if(sinfo->flags & IPT_CONNTRACK_STATUS) {
492 printf("%sctstatus ", optpfx);
493 if (sinfo->invflags & IPT_CONNTRACK_STATUS)
494 printf("! ");
495 print_status(sinfo->statusmask);
498 if(sinfo->flags & IPT_CONNTRACK_EXPIRES) {
499 printf("%sctexpire ", optpfx);
500 if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
501 printf("! ");
503 #ifdef KERNEL_64_USERSPACE_32
504 if (sinfo->expires_max == sinfo->expires_min)
505 printf("%llu ", sinfo->expires_min);
506 else
507 printf("%llu:%llu ", sinfo->expires_min, sinfo->expires_max);
508 #else
509 if (sinfo->expires_max == sinfo->expires_min)
510 printf("%lu ", sinfo->expires_min);
511 else
512 printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
513 #endif
517 /* Prints out the matchinfo. */
518 static void
519 print(const struct ipt_ip *ip,
520 const struct ipt_entry_match *match,
521 int numeric)
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 = {
533 .next = NULL,
534 .name = "conntrack",
535 .version = IPTABLES_VERSION,
536 .size = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
537 .userspacesize = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
538 .help = &help,
539 .parse = &parse,
540 .final_check = &final_check,
541 .print = &print,
542 .save = &save,
543 .extra_opts = opts
546 void _init(void)
548 register_match(&conntrack);