iptables-1.2.9-2.3.1.src.rpm
[iptables.git] / extensions / libipt_conntrack.c
1 /* Shared library add-on to iptables for conntrack matching support.
2  * GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
3  */
4
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_ipv4/ip_conntrack.h>
13 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
14 #include <linux/netfilter_ipv4/ipt_conntrack.h>
15
16 #ifndef IPT_CONNTRACK_STATE_UNTRACKED
17 #define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
18 #endif
19
20 /* Function which prints out usage message. */
21 static void
22 help(void)
23 {
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);
42 }
43
44
45
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}
56 };
57
58 /* Initialize the match. */
59 static void
60 init(struct ipt_entry_match *m, unsigned int *nfcache)
61 {
62         /* Can't cache this */
63         *nfcache |= NFC_UNKNOWN;
64 }
65
66 static int
67 parse_state(const char *state, size_t strlen, struct ipt_conntrack_info *sinfo)
68 {
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;
83         else
84                 return 0;
85         return 1;
86 }
87
88 static void
89 parse_states(const char *arg, struct ipt_conntrack_info *sinfo)
90 {
91         const char *comma;
92
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);
96                 arg = comma+1;
97         }
98
99         if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
100                 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
101 }
102
103 static int
104 parse_status(const char *status, size_t strlen, struct ipt_conntrack_info *sinfo)
105 {
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;
114 #ifdef IPS_CONFIRMED
115         else if (strncasecmp(status, "CONFIRMED", strlen) == 0)
116                 sinfo->stausmask |= IPS_CONFIRMED;
117 #endif
118         else
119                 return 0;
120         return 1;
121 }
122
123 static void
124 parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
125 {
126         const char *comma;
127
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);
131                 arg = comma+1;
132         }
133
134         if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
135                 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
136 }
137
138
139 static unsigned long
140 parse_expire(const char *s)
141 {
142         unsigned int len;
143         
144         if (string_to_number(s, 0, 0xFFFFFFFF, &len) == -1)
145                 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
146         else
147                 return len;
148 }
149
150 /* If a single value is provided, min and max are both set to the value */
151 static void
152 parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
153 {
154         char *buffer;
155         char *cp;
156
157         buffer = strdup(s);
158         if ((cp = strchr(buffer, ':')) == NULL)
159                 sinfo->expires_min = sinfo->expires_max = parse_expire(buffer);
160         else {
161                 *cp = '\0';
162                 cp++;
163
164                 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
165                 sinfo->expires_max = cp[0] ? parse_expire(cp) : 0xFFFFFFFF;
166         }
167         free(buffer);
168         
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);
173         
174 }
175
176 /* Function which parses command options; returns true if it
177    ate an option */
178 static int
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)
183 {
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;
188
189
190         switch (c) {
191         case '1':
192                 check_inverse(optarg, &invert, &optind, 0);
193
194                 parse_states(argv[optind-1], sinfo);
195                 if (invert) {
196                         sinfo->invflags |= IPT_CONNTRACK_STATE;
197                 }
198                 sinfo->flags |= IPT_CONNTRACK_STATE;
199                 break;
200
201         case '2':
202                 check_inverse(optarg, &invert, &optind, 0);
203
204                 if(invert)
205                         sinfo->invflags |= IPT_CONNTRACK_PROTO;
206
207                 /* Canonicalize into lower case */
208                 for (protocol = argv[optind-1]; *protocol; protocol++)
209                         *protocol = tolower(*protocol);
210
211                 protocol = argv[optind-1];
212                 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol);
213
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");
218
219                 sinfo->flags |= IPT_CONNTRACK_PROTO;
220                 break;
221
222         case '3':
223                 check_inverse(optarg, &invert, &optind, 9);
224
225                 if (invert)
226                         sinfo->invflags |= IPT_CONNTRACK_ORIGSRC;
227
228                 parse_hostnetworkmask(argv[optind-1], &addrs,
229                                         &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
230                                         &naddrs);
231                 if(naddrs > 1)
232                         exit_error(PARAMETER_PROBLEM,
233                                 "multiple IP addresses not allowed");
234
235                 if(naddrs == 1) {
236                         sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
237                 }
238
239                 sinfo->flags |= IPT_CONNTRACK_ORIGSRC;
240                 break;
241
242         case '4':
243                 check_inverse(optarg, &invert, &optind, 0);
244
245                 if (invert)
246                         sinfo->invflags |= IPT_CONNTRACK_ORIGDST;
247
248                 parse_hostnetworkmask(argv[optind-1], &addrs,
249                                         &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
250                                         &naddrs);
251                 if(naddrs > 1)
252                         exit_error(PARAMETER_PROBLEM,
253                                 "multiple IP addresses not allowed");
254
255                 if(naddrs == 1) {
256                         sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
257                 }
258
259                 sinfo->flags |= IPT_CONNTRACK_ORIGDST;
260                 break;
261
262         case '5':
263                 check_inverse(optarg, &invert, &optind, 0);
264
265                 if (invert)
266                         sinfo->invflags |= IPT_CONNTRACK_REPLSRC;
267
268                 parse_hostnetworkmask(argv[optind-1], &addrs,
269                                         &sinfo->sipmsk[IP_CT_DIR_REPLY],
270                                         &naddrs);
271                 if(naddrs > 1)
272                         exit_error(PARAMETER_PROBLEM,
273                                 "multiple IP addresses not allowed");
274
275                 if(naddrs == 1) {
276                         sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
277                 }
278
279                 sinfo->flags |= IPT_CONNTRACK_REPLSRC;
280                 break;
281
282         case '6':
283                 check_inverse(optarg, &invert, &optind, 0);
284
285                 if (invert)
286                         sinfo->invflags |= IPT_CONNTRACK_REPLDST;
287
288                 parse_hostnetworkmask(argv[optind-1], &addrs,
289                                         &sinfo->dipmsk[IP_CT_DIR_REPLY],
290                                         &naddrs);
291                 if(naddrs > 1)
292                         exit_error(PARAMETER_PROBLEM,
293                                 "multiple IP addresses not allowed");
294
295                 if(naddrs == 1) {
296                         sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
297                 }
298
299                 sinfo->flags |= IPT_CONNTRACK_REPLDST;
300                 break;
301
302         case '7':
303                 check_inverse(optarg, &invert, &optind, 0);
304
305                 parse_statuses(argv[optind-1], sinfo);
306                 if (invert) {
307                         sinfo->invflags |= IPT_CONNTRACK_STATUS;
308                 }
309                 sinfo->flags |= IPT_CONNTRACK_STATUS;
310                 break;
311
312         case '8':
313                 check_inverse(optarg, &invert, &optind, 0);
314
315                 parse_expires(argv[optind-1], sinfo);
316                 if (invert) {
317                         sinfo->invflags |= IPT_CONNTRACK_EXPIRES;
318                 }
319                 sinfo->flags |= IPT_CONNTRACK_EXPIRES;
320                 break;
321
322         default:
323                 return 0;
324         }
325
326         *flags = sinfo->flags;
327         return 1;
328 }
329
330 static void
331 final_check(unsigned int flags)
332 {
333         if (!flags)
334                 exit_error(PARAMETER_PROBLEM, "You must specify one or more options");
335 }
336
337 static void
338 print_state(unsigned int statemask)
339 {
340         const char *sep = "";
341
342         if (statemask & IPT_CONNTRACK_STATE_INVALID) {
343                 printf("%sINVALID", sep);
344                 sep = ",";
345         }
346         if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
347                 printf("%sNEW", sep);
348                 sep = ",";
349         }
350         if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
351                 printf("%sRELATED", sep);
352                 sep = ",";
353         }
354         if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
355                 printf("%sESTABLISHED", sep);
356                 sep = ",";
357         }
358         if (statemask & IPT_CONNTRACK_STATE_UNTRACKED) {
359                 printf("%sUNTRACKED", sep);
360                 sep = ",";
361         }
362         if (statemask & IPT_CONNTRACK_STATE_SNAT) {
363                 printf("%sSNAT", sep);
364                 sep = ",";
365         }
366         if (statemask & IPT_CONNTRACK_STATE_DNAT) {
367                 printf("%sDNAT", sep);
368                 sep = ",";
369         }
370         printf(" ");
371 }
372
373 static void
374 print_status(unsigned int statusmask)
375 {
376         const char *sep = "";
377
378         if (statusmask & IPS_EXPECTED) {
379                 printf("%sEXPECTED", sep);
380                 sep = ",";
381         }
382         if (statusmask & IPS_SEEN_REPLY) {
383                 printf("%sSEEN_REPLY", sep);
384                 sep = ",";
385         }
386         if (statusmask & IPS_ASSURED) {
387                 printf("%sASSURED", sep);
388                 sep = ",";
389         }
390 #ifdef IPS_CONFIRMED
391         if (statusmask & IPS_CONFIRMED) {
392                 printf("%sCONFIRMED", sep);
393                 sep =",";
394         }
395 #endif
396         if (statusmask == 0) {
397                 printf("%sNONE", sep);
398                 sep = ",";
399         }
400         printf(" ");
401 }
402
403 static void
404 print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
405 {
406         char buf[BUFSIZ];
407
408         if (inv)
409                 fputc('!', stdout);
410
411         if (mask->s_addr == 0L && !numeric)
412                 printf("%s ", "anywhere");
413         else {
414                 if (numeric)
415                         sprintf(buf, "%s", addr_to_dotted(addr));
416                 else
417                         sprintf(buf, "%s", addr_to_anyname(addr));
418                 strcat(buf, mask_to_dotted(mask));
419                 printf("%s ", buf);
420         }
421 }
422
423 /* Saves the matchinfo in parsable form to stdout. */
424 static void
425 matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric, const char *optpfx)
426 {
427         struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)match->data;
428
429         if(sinfo->flags & IPT_CONNTRACK_STATE) {
430                 printf("%sctstate ", optpfx);
431                 if (sinfo->invflags & IPT_CONNTRACK_STATE)
432                         printf("! ");
433                 print_state(sinfo->statemask);
434         }
435
436         if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) {
437                 printf("%sctorigsrc ", optpfx);
438
439                 print_addr(
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,
443                     numeric);
444         }
445
446         if(sinfo->flags & IPT_CONNTRACK_ORIGDST) {
447                 printf("%sctorigdst ", optpfx);
448
449                 print_addr(
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,
453                     numeric);
454         }
455
456         if(sinfo->flags & IPT_CONNTRACK_REPLSRC) {
457                 printf("%sctreplsrc ", optpfx);
458
459                 print_addr(
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,
463                     numeric);
464         }
465
466         if(sinfo->flags & IPT_CONNTRACK_REPLDST) {
467                 printf("%sctrepldst ", optpfx);
468
469                 print_addr(
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,
473                     numeric);
474         }
475
476         if(sinfo->flags & IPT_CONNTRACK_STATUS) {
477                 printf("%sctstatus ", optpfx);
478                 if (sinfo->invflags & IPT_CONNTRACK_STATE)
479                         printf("! ");
480                 print_status(sinfo->statusmask);
481         }
482
483         if(sinfo->flags & IPT_CONNTRACK_EXPIRES) {
484                 printf("%sctexpire ", optpfx);
485                 if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
486                         printf("! ");
487
488                 if (sinfo->expires_max == sinfo->expires_min)
489                         printf("%lu ", sinfo->expires_min);
490                 else
491                         printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
492         }
493 }
494
495 /* Prints out the matchinfo. */
496 static void
497 print(const struct ipt_ip *ip,
498       const struct ipt_entry_match *match,
499       int numeric)
500 {
501         matchinfo_print(ip, match, numeric, "");
502 }
503
504 /* Saves the matchinfo in parsable form to stdout. */
505 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
506 {
507         matchinfo_print(ip, match, 0, "--");
508 }
509
510 static
511 struct iptables_match conntrack
512 = { NULL,
513     "conntrack",
514     IPTABLES_VERSION,
515     IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
516     IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
517     &help,
518     &init,
519     &parse,
520     &final_check,
521     &print,
522     &save,
523     opts
524 };
525
526 void _init(void)
527 {
528         register_match(&conntrack);
529 }