merge up to iptables 1.3.8 with a basic set of extensions
[iptables.git] / extensions / libip6t_multiport.c
index 013241b..166abce 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);
 }
@@ -33,28 +51,21 @@ static struct option opts[] = {
        {0}
 };
 
-static int
-service_to_port(const char *name, const char *proto)
-{
-       struct servent *service;
-
-       if ((service = getservbyname(name, proto)) != NULL)
-               return ntohs((unsigned short) service->s_port);
-
-               return -1;
-}
-
-static u_int16_t
-parse_port(const char *port, const char *proto)
+static char *
+proto_to_name(u_int8_t proto)
 {
-       unsigned int portnum;
-
-       if ((string_to_number(port, 0, 65535, &portnum)) != -1 ||
-           (portnum = service_to_port(port, proto)) != -1)
-               return (u_int16_t)portnum;
-
-       exit_error(PARAMETER_PROBLEM,
-                  "invalid port/service `%s' specified", port);
+       switch (proto) {
+       case IPPROTO_TCP:
+               return "tcp";
+       case IPPROTO_UDP:
+               return "udp";
+       case IPPROTO_SCTP:
+               return "sctp";
+       case IPPROTO_DCCP:
+               return "dccp";
+       default:
+               return NULL;
+       }
 }
 
 static unsigned int
@@ -77,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)
@@ -86,16 +137,16 @@ init(struct ip6t_entry_match *m, unsigned int *nfcache)
 static const char *
 check_proto(const struct ip6t_entry *entry)
 {
-       if (entry->ipv6.proto == IPPROTO_TCP)
-               return "tcp";
-       else if (entry->ipv6.proto == IPPROTO_UDP)
-               return "udp";
+       char *proto;
+
+       if ((proto = proto_to_name(entry->ipv6.proto)) != NULL)
+               return proto;
        else if (!entry->ipv6.proto)
                exit_error(PARAMETER_PROBLEM,
-                          "multiport needs `-p tcp' or `-p udp'");
+                          "multiport needs `-p tcp', `-p udp', `-p sctp' or `-p dccp'");
        else
                exit_error(PARAMETER_PROBLEM,
-                          "multiport only works with TCP or UDP");
+                          "multiport only works with TCP, UDP, SCTP and DCCP");
 }
 
 /* Function which parses command options; returns true if it
@@ -150,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)
@@ -163,8 +260,7 @@ port_to_service(int port, u_int8_t proto)
 {
        struct servent *service;
 
-       if ((service = getservbyport(htons(port),
-                                    proto == IPPROTO_TCP ? "tcp" : "udp")))
+       if ((service = getservbyport(htons(port), proto_to_name(proto))))
                return service->s_name;
 
        return NULL;
@@ -218,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)
 {
@@ -246,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,
@@ -260,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);
 }