iptables-1.3.2-20050720
[iptables.git] / extensions / libipt_sctp.c
index d512fa6..af35f9c 100644 (file)
 #include <stdlib.h>
 #include <getopt.h>
 #include <netdb.h>
+#include <ctype.h>
 
 #include <iptables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_sctp.h>
 
+#if 0
+#define DEBUGP(format, first...) printf(format, ##first)
+#define static
+#else
+#define DEBUGP(format, fist...) 
+#endif
+
+static void
+print_chunk(u_int32_t chunknum, int numeric);
+
 /* Initialize the match. */
 static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
+init(struct ipt_entry_match *m, 
+     unsigned int *nfcache)
 {
+       int i;
        struct ipt_sctp_info *einfo = (struct ipt_sctp_info *)m->data;
 
-       einfo->spts[1] = einfo->dpts[1] = 0xFFFF;
+       memset(einfo, 0, sizeof(struct ipt_sctp_info));
+
+       for (i = 0; i < IPT_NUM_SCTP_FLAGS; i++) {
+               einfo->flag_info[i].chunktype = -1;
+       }
 }
 
-static void help(void) 
+static void help(void)
 {
        printf(
 "SCTP match v%s options\n"
-" --sctp-chunks [!] mask comp  match when SCTP chunks & mask == comp\n"
-" --source-port [!] port[:port]\n"
+" --source-port [!] port[:port]                          match source port(s)\n"
 " --sport ...\n"
-"                               match source port(s)"
-" --destination-port [!] port[:port]\n"
-" --dport ...\n\n",
+" --destination-port [!] port[:port]                     match destination port(s)\n"
+" --dport ...\n" 
+" --chunk-types [!] (all|any|none) (chunktype[:flags])+        match if all, any or none of\n"
+"                                                      chunktypes are present\n"
+"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK ALL NONE\n",
        IPTABLES_VERSION);
 }
 
@@ -44,7 +62,7 @@ static struct option opts[] = {
        { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
        { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
        { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
-       { .name = "sctp-chunks", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = "chunk-types", .has_arg = 1, .flag = 0, .val = '3' },
        { .name = 0 }
 };
 
@@ -64,24 +82,27 @@ parse_sctp_port(const char *port)
 {
        unsigned int portnum;
 
+       DEBUGP("%s\n", port);
        if (string_to_number(port, 0, 65535, &portnum) != -1 ||
            (portnum = service_to_port(port)) != -1)
                return (u_int16_t)portnum;
 
        exit_error(PARAMETER_PROBLEM,
-                  "invalid TCP port/service `%s' specified", port);
+                  "invalid SCTP port/service `%s' specified", port);
 }
 
-
 static void
-parse_sctp_ports(const char *portstring, u_int16_t *ports)
+parse_sctp_ports(const char *portstring, 
+                u_int16_t *ports)
 {
        char *buffer;
        char *cp;
 
        buffer = strdup(portstring);
-       if ((cp = strchr(buffer, ':')) == NULL)
+       DEBUGP("%s\n", portstring);
+       if ((cp = strchr(buffer, ':')) == NULL) {
                ports[0] = ports[1] = parse_sctp_port(buffer);
+       }
        else {
                *cp = '\0';
                cp++;
@@ -98,51 +119,106 @@ parse_sctp_ports(const char *portstring, u_int16_t *ports)
 
 struct sctp_chunk_names {
        const char *name;
-       unsigned int flag;
+       unsigned int chunk_type;
+       const char *valid_flags;
 };
 
-/* FIXME: */
-#define ALL_CHUNKS     0xabcdef
+/*'ALL' and 'NONE' will be treated specially. */
 static struct sctp_chunk_names sctp_chunk_names[]
-= { { .name = "DATA",          .flag = (1 << 0) },
-    { .name = "INIT",          .flag = (1 << 1) },
-    { .name = "INIT_ACK",      .flag = (1 << 2) },
-    { .name = "SACK",          .flag = (1 << 3) },
-    { .name = "HEARTBEAT",     .flag = (1 << 4) },
-    { .name = "HEARTBEAT_ACK", .flag = (1 << 5) },
-    { .name = "ABORT",         .flag = (1 << 6) },
-    { .name = "SHUTDOWN",      .flag = (1 << 7) },
-    { .name = "SHUTDOWN_ACK",  .flag = (1 << 8) },
-    { .name = "ERROR",         .flag = (1 << 9) },
-    { .name = "COOKIE_ECHO",   .flag = (1 << 10) },
-    { .name = "COOKIE_ACK",    .flag = (1 << 11) },
-    { .name = "ECN_ECNE",      .flag = (1 << 12) },
-    { .name = "ECN_CWR",       .flag = (1 << 13) },
-    { .name = "SHUTDOWN_COMPLETE", .flag = (1 << 14) },
-    { .name = "ASCONF",                .flag = (1 << 31) },
-    { .name = "ASCONF_ACK",    .flag = (1 << 30) },
-    { .name = "ALL",           .flag = ALL_CHUNKS },
-    { .name = "NONE",          .flag = 0 },
+= { { .name = "DATA",          .chunk_type = 0,   .valid_flags = "-----UBE"},
+    { .name = "INIT",          .chunk_type = 1,   .valid_flags = "--------"},
+    { .name = "INIT_ACK",      .chunk_type = 2,   .valid_flags = "--------"},
+    { .name = "SACK",          .chunk_type = 3,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT",     .chunk_type = 4,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT_ACK", .chunk_type = 5,   .valid_flags = "--------"},
+    { .name = "ABORT",         .chunk_type = 6,   .valid_flags = "-------T"},
+    { .name = "SHUTDOWN",      .chunk_type = 7,   .valid_flags = "--------"},
+    { .name = "SHUTDOWN_ACK",  .chunk_type = 8,   .valid_flags = "--------"},
+    { .name = "ERROR",         .chunk_type = 9,   .valid_flags = "--------"},
+    { .name = "COOKIE_ECHO",   .chunk_type = 10,  .valid_flags = "--------"},
+    { .name = "COOKIE_ACK",    .chunk_type = 11,  .valid_flags = "--------"},
+    { .name = "ECN_ECNE",      .chunk_type = 12,  .valid_flags = "--------"},
+    { .name = "ECN_CWR",       .chunk_type = 13,  .valid_flags = "--------"},
+    { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14,  .valid_flags = "-------T"},
+    { .name = "ASCONF",                .chunk_type = 31,  .valid_flags = "--------"},
+    { .name = "ASCONF_ACK",    .chunk_type = 30,  .valid_flags = "--------"},
 };
 
+static void
+save_chunk_flag_info(struct ipt_sctp_flag_info *flag_info,
+                    int *flag_count,
+                    int chunktype, 
+                    int bit, 
+                    int set)
+{
+       int i;
+
+       for (i = 0; i < *flag_count; i++) {
+               if (flag_info[i].chunktype == chunktype) {
+                       DEBUGP("Previous match found\n");
+                       flag_info[i].chunktype = chunktype;
+                       flag_info[i].flag_mask |= (1 << bit);
+                       if (set) {
+                               flag_info[i].flag |= (1 << bit);
+                       }
+
+                       return;
+               }
+       }
+       
+       if (*flag_count == IPT_NUM_SCTP_FLAGS) {
+               exit_error (PARAMETER_PROBLEM,
+                       "Number of chunk types with flags exceeds currently allowed limit."
+                       "Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and"
+                       "recompiling both the kernel space and user space modules\n");
+       }
+
+       flag_info[*flag_count].chunktype = chunktype;
+       flag_info[*flag_count].flag_mask |= (1 << bit);
+       if (set) {
+               flag_info[*flag_count].flag |= (1 << bit);
+       }
+       (*flag_count)++;
+}
 
-static unsigned int
-parse_sctp_chunk(const char *flags)
+static void
+parse_sctp_chunk(struct ipt_sctp_info *einfo, 
+                const char *chunks)
 {
-       unsigned int ret = 0;
        char *ptr;
        char *buffer;
+       unsigned int i, j;
+       int found = 0;
+       char *chunk_flags;
+
+       buffer = strdup(chunks);
+       DEBUGP("Buffer: %s\n", buffer);
 
-       buffer = strdup(flags);
+       SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+
+       if (!strcasecmp(buffer, "ALL")) {
+               SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap);
+               goto out;
+       }
+       
+       if (!strcasecmp(buffer, "NONE")) {
+               SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+               goto out;
+       }
 
        for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
-               unsigned int i;
-               int found = 0;
-               for (i = 0;
-                    i < sizeof(sctp_chunk_names)/sizeof(struct sctp_chunk_names);
-                    i++) {
+               found = 0;
+               DEBUGP("Next Chunk type %s\n", ptr);
+               
+               if ((chunk_flags = strchr(ptr, ':')) != NULL) {
+                       *chunk_flags++ = 0;
+               }
+               
+               for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
                        if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
-                               ret |= sctp_chunk_names[i].flag;
+                               DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type);
+                               SCTP_CHUNKMAP_SET(einfo->chunkmap, 
+                                       sctp_chunk_names[i].chunk_type);
                                found = 1;
                                break;
                        }
@@ -150,29 +226,53 @@ parse_sctp_chunk(const char *flags)
                if (!found)
                        exit_error(PARAMETER_PROBLEM,
                                   "Unknown sctp chunk `%s'", ptr);
-       }
 
+               if (chunk_flags) {
+                       DEBUGP("Chunk flags %s\n", chunk_flags);
+                       for (j = 0; j < strlen(chunk_flags); j++) {
+                               char *p;
+                               int bit;
+
+                               if ((p = strchr(sctp_chunk_names[i].valid_flags, 
+                                               toupper(chunk_flags[j]))) != NULL) {
+                                       bit = p - sctp_chunk_names[i].valid_flags;
+                                       bit = 7 - bit;
+
+                                       save_chunk_flag_info(einfo->flag_info, 
+                                               &(einfo->flag_count), i, bit, 
+                                               isupper(chunk_flags[j]));
+                               } else {
+                                       exit_error(PARAMETER_PROBLEM, 
+                                               "Invalid flags for chunk type %d\n", i);
+                               }
+                       }
+               }
+       }
+out:
        free(buffer);
-       return ret;
 }
 
 static void
 parse_sctp_chunks(struct ipt_sctp_info *einfo,
-               const char *mask,
-               const char *cmp,
-               int invert)
+                 const char *match_type,
+                 const char *chunks)
 {
-       einfo->chunks = parse_sctp_chunk(mask);
-       einfo->chunk_mask = parse_sctp_chunk(cmp);
+       DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks);
+       if (!strcasecmp(match_type, "ANY")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY;
+       } else  if (!strcasecmp(match_type, "ALL")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL;
+       } else  if (!strcasecmp(match_type, "ONLY")) {
+               einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY;
+       } else {
+               exit_error (PARAMETER_PROBLEM, 
+                       "Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\"");
+       }
 
-       if (invert)
-               einfo->invflags |= IPT_SCTP_INV_CHUNKS;
+       SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+       parse_sctp_chunk(einfo, chunks);
 }
 
-#define SCTP_SRC_PORTS 0x01
-#define SCTP_DST_PORTS 0x02
-#define SCTP_CHUNKS    0x03
-
 static int
 parse(int c, char **argv, int invert, unsigned int *flags,
       const struct ipt_entry *entry,
@@ -184,48 +284,51 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 
        switch (c) {
        case '1':
-               if (*flags & SCTP_SRC_PORTS)
+               if (*flags & IPT_SCTP_SRC_PORTS)
                        exit_error(PARAMETER_PROBLEM,
                                   "Only one `--source-port' allowed");
+               einfo->flags |= IPT_SCTP_SRC_PORTS;
                check_inverse(optarg, &invert, &optind, 0);
                parse_sctp_ports(argv[optind-1], einfo->spts);
                if (invert)
-                       einfo->invflags |= IPT_SCTP_INV_SRCPT;
-               *flags |= SCTP_SRC_PORTS;
-               *nfcache |= NFC_IP_SRC_PT;
+                       einfo->invflags |= IPT_SCTP_SRC_PORTS;
+               *flags |= IPT_SCTP_SRC_PORTS;
                break;
 
        case '2':
-               if (*flags & SCTP_DST_PORTS)
+               if (*flags & IPT_SCTP_DEST_PORTS)
                        exit_error(PARAMETER_PROBLEM,
                                   "Only one `--destination-port' allowed");
+               einfo->flags |= IPT_SCTP_DEST_PORTS;
                check_inverse(optarg, &invert, &optind, 0);
                parse_sctp_ports(argv[optind-1], einfo->dpts);
                if (invert)
-                       einfo->invflags |= IPT_SCTP_INV_DSTPT;
-               *flags |= SCTP_DST_PORTS;
-               *nfcache |= NFC_IP_DST_PT;
+                       einfo->invflags |= IPT_SCTP_DEST_PORTS;
+               *flags |= IPT_SCTP_DEST_PORTS;
                break;
 
        case '3':
-               if (*flags & SCTP_CHUNKS)
+               if (*flags & IPT_SCTP_CHUNK_TYPES)
                        exit_error(PARAMETER_PROBLEM,
-                                  "Only one `--sctp-chunks' allowed");
+                                  "Only one `--chunk-types' allowed");
                check_inverse(optarg, &invert, &optind, 0);
 
                if (!argv[optind] 
                    || argv[optind][0] == '-' || argv[optind][0] == '!')
                        exit_error(PARAMETER_PROBLEM,
-                                  "--sctp-chunks requires two args");
+                                  "--chunk-types requires two args");
 
-               parse_sctp_chunks(einfo, argv[optind-1], argv[optind], invert);
+               einfo->flags |= IPT_SCTP_CHUNK_TYPES;
+               parse_sctp_chunks(einfo, argv[optind-1], argv[optind]);
+               if (invert)
+                       einfo->invflags |= IPT_SCTP_CHUNK_TYPES;
                optind++;
-               *flags |= SCTP_CHUNKS;
+               *flags |= IPT_SCTP_CHUNK_TYPES;
                break;
+
        default:
                return 0;
        }
-
        return 1;
 }
 
@@ -278,41 +381,89 @@ print_ports(const char *name, u_int16_t min, u_int16_t max,
 }
 
 static void
-print_chunk(u_int32_t chunks)
+print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask)
 {
-       unsigned int have_flag = 0;
+       int i;
 
-       while (chunks) {
-               unsigned int i;
+       DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, 
+                       chunk_flags_mask);
 
-               for (i = 0; (chunks & sctp_chunk_names[i].flag) == 0; i++);
+       if (chunk_flags_mask) {
+               printf(":");
+       }
 
-               if (have_flag)
-                       printf(",");
-               printf("%s", sctp_chunk_names[i].name);
-               have_flag = 1;
+       for (i = 7; i >= 0; i--) {
+               if (chunk_flags_mask & (1 << i)) {
+                       if (chunk_flags & (1 << i)) {
+                               printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
+                       } else {
+                               printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
+                       }
+               }
+       }
+}
 
-               chunks &= ~sctp_chunk_names[i].flag;
+static void
+print_chunk(u_int32_t chunknum, int numeric)
+{
+       if (numeric) {
+               printf("0x%04X", chunknum);
        }
+       else {
+               int i;
 
-       if (!have_flag)
-               printf("NONE");
+               for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
+                       if (sctp_chunk_names[i].chunk_type == chunknum)
+                               printf("%s", sctp_chunk_names[chunknum].name);
+               }
+       }
 }
 
 static void
-print_chunks(u_int32_t mask, u_int32_t cmp, int invert, int numeric)
+print_chunks(u_int32_t chunk_match_type, 
+            const u_int32_t *chunkmap, 
+            const struct ipt_sctp_flag_info *flag_info,
+            int flag_count,
+            int numeric)
 {
-       if (mask || invert) {
-               printf("flags:%s", invert ? "!" : "");
-               if (numeric)
-                       printf("0x%04X/0x%04X ", mask, cmp);
-               else {
-                       print_chunk(mask);
-                       printf("/");
-                       print_chunk(cmp);
-                       printf(" ");
+       int i, j;
+       int flag;
+
+       switch (chunk_match_type) {
+               case SCTP_CHUNK_MATCH_ANY:      printf("any "); break;
+               case SCTP_CHUNK_MATCH_ALL:      printf("all "); break;
+               case SCTP_CHUNK_MATCH_ONLY:     printf("only "); break;
+               default:        printf("Never reach herer\n"); break;
+       }
+
+       if (SCTP_CHUNKMAP_IS_CLEAR(chunkmap)) {
+               printf("NONE ");
+               goto out;
+       }
+       
+       if (SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)) {
+               printf("ALL ");
+               goto out;
+       }
+       
+       flag = 0;
+       for (i = 0; i < 256; i++) {
+               if (SCTP_CHUNKMAP_IS_SET(chunkmap, i)) {
+                       flag && printf(",");
+                       flag = 1;
+                       print_chunk(i, numeric);
+                       for (j = 0; j < flag_count; j++) {
+                               if (flag_info[j].chunktype == i) {
+                                       print_chunk_flags(i, flag_info[j].flag,
+                                               flag_info[j].flag_mask);
+                               }
+                       }
                }
        }
+
+       flag && printf(" ");
+out:
+       return;
 }
 
 /* Prints out the matchinfo. */
@@ -326,28 +477,39 @@ print(const struct ipt_ip *ip,
 
        printf("sctp ");
 
-       print_ports("spt", einfo->spts[0], einfo->spts[1],
-                   einfo->invflags & IPT_SCTP_INV_SRCPT,
-                   numeric);
-       print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
-                   einfo->invflags & IPT_SCTP_INV_DSTPT,
-                   numeric);
+       if (einfo->flags & IPT_SCTP_SRC_PORTS) {
+               print_ports("spt", einfo->spts[0], einfo->spts[1],
+                       einfo->invflags & IPT_SCTP_SRC_PORTS,
+                       numeric);
+       }
 
-       print_chunks(einfo->chunks, einfo->chunk_mask,
-                    einfo->invflags & ~IPT_SCTP_INV_MASK,
-                    numeric);
+       if (einfo->flags & IPT_SCTP_DEST_PORTS) {
+               print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+                       einfo->invflags & IPT_SCTP_DEST_PORTS,
+                       numeric);
+       }
+
+       if (einfo->flags & IPT_SCTP_CHUNK_TYPES) {
+               /* FIXME: print_chunks() is used in save() where the printing of '!'
+               s taken care of, so we need to do that here as well */
+               if (einfo->invflags & IPT_SCTP_CHUNK_TYPES) {
+                       printf("! ");
+               }
+               print_chunks(einfo->chunk_match_type, einfo->chunkmap,
+                       einfo->flag_info, einfo->flag_count, numeric);
+       }
 }
 
 /* Saves the union ipt_matchinfo in parsable form to stdout. */
 static void
-save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+save(const struct ipt_ip *ip, 
+     const struct ipt_entry_match *match)
 {
        const struct ipt_sctp_info *einfo =
                (const struct ipt_sctp_info *)match->data;
 
-       if (einfo->spts[0] != 0
-           || einfo->spts[1] != 0xFFFF) {
-               if (einfo->invflags & IPT_SCTP_INV_SRCPT)
+       if (einfo->flags & IPT_SCTP_SRC_PORTS) {
+               if (einfo->invflags & IPT_SCTP_SRC_PORTS)
                        printf("! ");
                if (einfo->spts[0] != einfo->spts[1])
                        printf("--sport %u:%u ", 
@@ -356,9 +518,8 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
                        printf("--sport %u ", einfo->spts[0]);
        }
 
-       if (einfo->dpts[0] != 0
-           || einfo->dpts[1] != 0xFFFF) {
-               if (einfo->invflags & IPT_SCTP_INV_DSTPT)
+       if (einfo->flags & IPT_SCTP_DEST_PORTS) {
+               if (einfo->invflags & IPT_SCTP_DEST_PORTS)
                        printf("! ");
                if (einfo->dpts[0] != einfo->dpts[1])
                        printf("--dport %u:%u ",
@@ -367,17 +528,13 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
                        printf("--dport %u ", einfo->dpts[0]);
        }
 
-       if (einfo->chunks
-           || (einfo->invflags & IPT_SCTP_INV_CHUNKS)) {
-               if (einfo->invflags & IPT_SCTP_INV_CHUNKS)
+       if (einfo->flags & IPT_SCTP_CHUNK_TYPES) {
+               if (einfo->invflags & IPT_SCTP_CHUNK_TYPES)
                        printf("! ");
-               printf("--sctp-chunks ");
-               if (einfo->chunks != ALL_CHUNKS) {
-                       print_chunk(einfo->chunks);
-               }
-               printf(" ");
-               print_chunk(einfo->chunk_mask);
-               printf(" ");
+               printf("--chunk-types ");
+
+               print_chunks(einfo->chunk_match_type, einfo->chunkmap, 
+                       einfo->flag_info, einfo->flag_count, 0);
        }
 }
 
@@ -400,3 +557,4 @@ void _init(void)
 {
        register_match(&sctp);
 }
+