]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
- Add revision support to ip6tables.
authorRémi Denis-Courmont <rdenis@simphalempin.com>
Fri, 20 Oct 2006 12:24:34 +0000 (12:24 +0000)
committerYasuyuki KOZAKAI <yasuyuki@netfilter.org>
Fri, 20 Oct 2006 12:24:34 +0000 (12:24 +0000)
- Add support port range match to libip6t_multiport
(R?mi Denis-Courmont <rdenis@simphalempin.com>)

extensions/libip6t_multiport.c
include/ip6tables.h
include/linux/netfilter_ipv6/ip6t_multiport.h [new file with mode: 0644]
ip6tables.c

index ed5fffeee31da366db7a43e1cb2364392888f2e9..166abce167d58752e4992310cca3b8b3df167b34 100644 (file)
@@ -5,7 +5,8 @@
 #include <stdlib.h>
 #include <getopt.h>
 #include <ip6tables.h>
-#include <linux/netfilter_ipv6/ip6t_multiport.h>
+/* To ensure that iptables compiles with an old kernel */
+#include "../include/linux/netfilter_ipv6/ip6t_multiport.h"
 
 /* Function which prints out usage message. */
 static void
@@ -20,6 +21,23 @@ help(void)
 " --dports ...\n"
 "                              match destination port(s)\n"
 " --ports port[,port,port]\n"
+"                              match both source and destination port(s)\n"
+" NOTE: this kernel does not support port ranges in multiport.\n",
+IPTABLES_VERSION);
+}
+
+static void
+help_v1(void)
+{
+       printf(
+"multiport v%s options:\n"
+" --source-ports [!] port[,port:port,port...]\n"
+" --sports ...\n"
+"                              match source port(s)\n"
+" --destination-ports [!] port[,port:port,port...]\n"
+" --dports ...\n"
+"                              match destination port(s)\n"
+" --ports [!] port[,port:port,port]\n"
 "                              match both source and destination port(s)\n",
 IPTABLES_VERSION);
 }
@@ -70,6 +88,46 @@ parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
        return i;
 }
 
+static void
+parse_multi_ports_v1(const char *portstring, 
+                    struct ip6t_multiport_v1 *multiinfo,
+                    const char *proto)
+{
+       char *buffer, *cp, *next, *range;
+       unsigned int i;
+       u_int16_t m;
+
+       buffer = strdup(portstring);
+       if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+       for (i=0; i<IP6T_MULTI_PORTS; i++)
+               multiinfo->pflags[i] = 0;
+       for (cp=buffer, i=0; cp && i<IP6T_MULTI_PORTS; cp=next, i++) {
+               next=strchr(cp, ',');
+               if (next) *next++='\0';
+               range = strchr(cp, ':');
+               if (range) {
+                       if (i == IP6T_MULTI_PORTS-1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "too many ports specified");
+                       *range++ = '\0';
+               }
+               multiinfo->ports[i] = parse_port(cp, proto);
+               if (range) {
+                       multiinfo->pflags[i] = 1;
+                       multiinfo->ports[++i] = parse_port(range, proto);
+                       if (multiinfo->ports[i-1] >= multiinfo->ports[i])
+                               exit_error(PARAMETER_PROBLEM,
+                                          "invalid portrange specified");
+                       m <<= 1;
+               }
+       }
+       multiinfo->count = i;
+       if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
+       free(buffer);
+}
+
 /* Initialize the match. */
 static void
 init(struct ip6t_entry_match *m, unsigned int *nfcache)
@@ -143,6 +201,52 @@ parse(int c, char **argv, int invert, unsigned int *flags,
        return 1;
 }
 
+static int
+parse_v1(int c, char **argv, int invert, unsigned int *flags,
+        const struct ip6t_entry *entry,
+        unsigned int *nfcache,
+        struct ip6t_entry_match **match)
+{
+       const char *proto;
+       struct ip6t_multiport_v1 *multiinfo
+               = (struct ip6t_multiport_v1 *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IP6T_MULTIPORT_SOURCE;
+               break;
+
+       case '2':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IP6T_MULTIPORT_DESTINATION;
+               break;
+
+       case '3':
+               check_inverse(argv[optind-1], &invert, &optind, 0);
+               proto = check_proto(entry);
+               parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
+               multiinfo->flags = IP6T_MULTIPORT_EITHER;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (invert)
+               multiinfo->invert = 1;
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "multiport can only have one option");
+       *flags = 1;
+       return 1;
+}
+
 /* Final check; must specify something. */
 static void
 final_check(unsigned int flags)
@@ -210,6 +314,49 @@ print(const struct ip6t_ip6 *ip,
        printf(" ");
 }
 
+static void
+print_v1(const struct ip6t_ip6 *ip,
+        const struct ip6t_entry_match *match,
+        int numeric)
+{
+       const struct ip6t_multiport_v1 *multiinfo
+               = (const struct ip6t_multiport_v1 *)match->data;
+       unsigned int i;
+
+       printf("multiport ");
+
+       switch (multiinfo->flags) {
+       case IP6T_MULTIPORT_SOURCE:
+               printf("sports ");
+               break;
+
+       case IP6T_MULTIPORT_DESTINATION:
+               printf("dports ");
+               break;
+
+       case IP6T_MULTIPORT_EITHER:
+               printf("ports ");
+               break;
+
+       default:
+               printf("ERROR ");
+               break;
+       }
+
+       if (multiinfo->invert)
+               printf("! ");
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, numeric);
+               if (multiinfo->pflags[i]) {
+                       printf(":");
+                       print_port(multiinfo->ports[++i], ip->proto, numeric);
+               }
+       }
+       printf(" ");
+}
+
 /* Saves the union ip6t_matchinfo in parsable form to stdout. */
 static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
 {
@@ -238,6 +385,41 @@ static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match
        printf(" ");
 }
 
+static void save_v1(const struct ip6t_ip6 *ip, 
+                   const struct ip6t_entry_match *match)
+{
+       const struct ip6t_multiport_v1 *multiinfo
+               = (const struct ip6t_multiport_v1 *)match->data;
+       unsigned int i;
+
+       switch (multiinfo->flags) {
+       case IP6T_MULTIPORT_SOURCE:
+               printf("--sports ");
+               break;
+
+       case IP6T_MULTIPORT_DESTINATION:
+               printf("--dports ");
+               break;
+
+       case IP6T_MULTIPORT_EITHER:
+               printf("--ports ");
+               break;
+       }
+
+       if (multiinfo->invert)
+               printf("! ");
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, 1);
+               if (multiinfo->pflags[i]) {
+                       printf(":");
+                       print_port(multiinfo->ports[++i], ip->proto, 1);
+               }
+       }
+       printf(" ");
+}
+
 static struct ip6tables_match multiport = {
        .name           = "multiport",
        .version        = IPTABLES_VERSION,
@@ -252,8 +434,25 @@ static struct ip6tables_match multiport = {
        .extra_opts     = opts,
 };
 
+static struct ip6tables_match multiport_v1 = { 
+       .next           = NULL,
+       .name           = "multiport",
+       .revision       = 1,
+       .version        = IPTABLES_VERSION,
+       .size           = IP6T_ALIGN(sizeof(struct ip6t_multiport_v1)),
+       .userspacesize  = IP6T_ALIGN(sizeof(struct ip6t_multiport_v1)),
+       .help           = &help_v1,
+       .init           = &init,
+       .parse          = &parse_v1,
+       .final_check    = &final_check,
+       .print          = &print_v1,
+       .save           = &save_v1,
+       .extra_opts     = opts
+};
+
 void
 _init(void)
 {
        register_match6(&multiport);
+       register_match6(&multiport_v1);
 }
index b1140b33d07483f0e8f9b16c6e10eae7613a5c62..406f25582fcdcb3e2a5c44ee47247b6f5c1d4923 100644 (file)
 #define IPPROTO_DCCP 33
 #endif
 
+#ifndef IP6T_SO_GET_REVISION_MATCH /* Old kernel source. */
+#define IP6T_SO_GET_REVISION_MATCH     (IP6T_BASE_CTL + 2)
+#define IP6T_SO_GET_REVISION_TARGET    (IP6T_BASE_CTL + 3)
+
+struct ip6t_get_revision
+{
+       char name[IP6T_FUNCTION_MAXNAMELEN-1];
+
+       u_int8_t revision;
+};
+#endif /* IP6T_SO_GET_REVISION_MATCH   Old kernel source */
+
 struct ip6tables_rule_match
 {
        struct ip6tables_rule_match *next;
@@ -33,6 +45,9 @@ struct ip6tables_match
 
        ip6t_chainlabel name;
 
+       /* Revision of match (0 by default). */
+       u_int8_t revision;
+
        const char *version;
 
        /* Size of match data. */
diff --git a/include/linux/netfilter_ipv6/ip6t_multiport.h b/include/linux/netfilter_ipv6/ip6t_multiport.h
new file mode 100644 (file)
index 0000000..8c2cc9d
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _IP6T_MULTIPORT_H
+#define _IP6T_MULTIPORT_H
+
+enum ip6t_multiport_flags
+{
+       IP6T_MULTIPORT_SOURCE,
+       IP6T_MULTIPORT_DESTINATION,
+       IP6T_MULTIPORT_EITHER
+};
+
+#define IP6T_MULTI_PORTS       15
+
+/* Must fit inside union xt_matchinfo: 16 bytes */
+struct ip6t_multiport
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[IP6T_MULTI_PORTS];      /* Ports */
+};
+
+struct ip6t_multiport_v1
+{
+       u_int8_t flags;                         /* Type of comparison */
+       u_int8_t count;                         /* Number of ports */
+       u_int16_t ports[IP6T_MULTI_PORTS];      /* Ports */
+       u_int8_t pflags[IP6T_MULTI_PORTS];      /* Port flags */
+       u_int8_t invert;                        /* Invert flag */
+};
+
+#endif /*_IP6T_MULTIPORT_H*/
index 659041f0e99420135cdb3b127a55c5c71a322102..92e434cfdcb3145153ae7db569a07a07f3eb1fed 100644 (file)
@@ -1102,10 +1102,51 @@ merge_options(struct option *oldopts, const struct option *newopts,
        return merge;
 }
 
+static int compatible_revision(const char *name, u_int8_t revision, int opt)
+{
+       struct ip6t_get_revision rev;
+       socklen_t s = sizeof(rev);
+       int max_rev, sockfd;
+
+       sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0) {
+               fprintf(stderr, "Could not open socket to kernel: %s\n",
+                       strerror(errno));
+               exit(1);
+       }
+
+       strcpy(rev.name, name);
+       rev.revision = revision;
+
+       max_rev = getsockopt(sockfd, IPPROTO_IPV6, opt, &rev, &s);
+       if (max_rev < 0) {
+               /* Definitely don't support this? */
+               if (errno == EPROTONOSUPPORT) {
+                       close(sockfd);
+                       return 0;
+               } else if (errno == ENOPROTOOPT) {
+                       close(sockfd);
+                       /* Assume only revision 0 support (old kernel) */
+                       return (revision == 0);
+               } else {
+                       fprintf(stderr, "getsockopt failed strangely: %s\n",
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       close(sockfd);
+       return 1;
+}
+
+static int compatible_match_revision(const char *name, u_int8_t revision)
+{
+       return compatible_revision(name, revision, IP6T_SO_GET_REVISION_MATCH);
+}
+
 void
 register_match6(struct ip6tables_match *me)
 {
-       struct ip6tables_match **i;
+       struct ip6tables_match **i, *old;
 
        if (strcmp(me->version, program_version) != 0) {
                fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
@@ -1113,12 +1154,36 @@ register_match6(struct ip6tables_match *me)
                exit(1);
        }
 
-       if (find_match(me->name, DURING_LOAD, NULL)) {
-               fprintf(stderr, "%s: match `%s' already registered.\n",
+       /* Revision field stole a char from name. */
+       if (strlen(me->name) >= IP6T_FUNCTION_MAXNAMELEN-1) {
+               fprintf(stderr, "%s: target `%s' has invalid name\n",
                        program_name, me->name);
                exit(1);
        }
 
+       old = find_match(me->name, DURING_LOAD, NULL);
+       if (old) {
+               if (old->revision == me->revision) {
+                       fprintf(stderr,
+                               "%s: match `%s' already registered.\n",
+                               program_name, me->name);
+                       exit(1);
+               }
+
+               /* Now we have two (or more) options, check compatibility. */
+               if (compatible_match_revision(old->name, old->revision)
+                   && old->revision > me->revision)
+                       return;
+
+               /* Replace if compatible. */
+               if (!compatible_match_revision(me->name, me->revision))
+                       return;
+
+               /* Delete old one. */
+               for (i = &ip6tables_matches; *i!=old; i = &(*i)->next);
+               *i = old->next;
+       }
+       
        if (me->size != IP6T_ALIGN(me->size)) {
                fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
                        program_name, me->name, (unsigned int)me->size);
@@ -1761,6 +1826,14 @@ void clear_rule_matches(struct ip6tables_rule_match **matches)
        *matches = NULL;
 }
 
+static void set_revision(char *name, u_int8_t revision)
+{
+       /* Old kernel sources don't have ".revision" field,
+          but we stole a byte from name. */
+       name[IP6T_FUNCTION_MAXNAMELEN - 2] = '\0';
+       name[IP6T_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
 int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
 {
        struct ip6t_entry fw, *e = NULL;
@@ -2041,6 +2114,7 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                        m->m = fw_calloc(1, size);
                        m->m->u.match_size = size;
                        strcpy(m->m->u.user.name, m->name);
+                       set_revision(m->m->u.user.name, m->revision);
                        if (m->init != NULL)
                                m->init(m->m, &fw.nfcache);
                        if (m != m->next)
@@ -2185,6 +2259,8 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
                                        m->m = fw_calloc(1, size);
                                        m->m->u.match_size = size;
                                        strcpy(m->m->u.user.name, m->name);
+                                       set_revision(m->m->u.user.name,
+                                                    m->revision);
                                        if (m->init != NULL)
                                                m->init(m->m, &fw.nfcache);