tags 329775 + patch thanks > The multiport match has > two versions, and the new "v1" one would be required. Unfortunately, > ip6tables userland does not support versioning currently (AFAICT, not > even in iptables SVN), which means we're stuck with version 0.
Patch against iptables tarball attached. Tested against kernel 2.6.17.x. -- Rémi Denis-Courmont http://www.remlab.net/
diff -Nru iptables-1.3.5.orig/extensions/libip6t_multiport.c iptables-1.3.5/extensions/libip6t_multiport.c --- iptables-1.3.5.orig/extensions/libip6t_multiport.c 2005-02-19 21:19:17.000000000 +0200 +++ iptables-1.3.5/extensions/libip6t_multiport.c 2006-08-27 13:03:36.000000000 +0300 @@ -20,6 +20,23 @@ " --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); } @@ -49,7 +66,7 @@ { unsigned int portnum; - if ((string_to_number(port, 0, 65535, &portnum)) != -1 || + if (string_to_number(port, 0, 65535, &portnum) != -1 || (portnum = service_to_port(port, proto)) != -1) return (u_int16_t)portnum; @@ -77,6 +94,46 @@ return i; } +static void +parse_multi_ports_v1(const char *portstring, + struct xt_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<XT_MULTI_PORTS; i++) + multiinfo->pflags[i] = 0; + + for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next, i++) { + next=strchr(cp, ','); + if (next) *next++='\0'; + range = strchr(cp, ':'); + if (range) { + if (i == XT_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) @@ -150,6 +207,52 @@ 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 xt_multiport_v1 *multiinfo + = (struct xt_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) @@ -218,6 +321,49 @@ printf(" "); } +static void +print_v1(const struct ip6t_ip6 *ip, + const struct ip6t_entry_match *match, + int numeric) +{ + const struct xt_multiport_v1 *multiinfo + = (const struct xt_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 +392,41 @@ printf(" "); } +static void save_v1(const struct ip6t_ip6 *ip, + const struct ip6t_entry_match *match) +{ + const struct xt_multiport_v1 *multiinfo + = (const struct xt_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 +441,25 @@ .extra_opts = opts, }; +static struct ip6tables_match multiport_v1 = { + .next = NULL, + .name = "multiport", + .revision = 1, + .version = IPTABLES_VERSION, + .size = IP6T_ALIGN(sizeof(struct xt_multiport_v1)), + .userspacesize = IP6T_ALIGN(sizeof(struct xt_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); } diff -Nru iptables-1.3.5.orig/include/ip6tables.h iptables-1.3.5/include/ip6tables.h --- iptables-1.3.5.orig/include/ip6tables.h 2006-01-30 10:43:09.000000000 +0200 +++ iptables-1.3.5/include/ip6tables.h 2006-08-27 13:02:55.000000000 +0300 @@ -22,6 +22,9 @@ ip6t_chainlabel name; + /* Revision of match (0 by default). */ + u_int8_t revision; + const char *version; /* Size of match data. */ diff -Nru iptables-1.3.5.orig/ip6tables.c iptables-1.3.5/ip6tables.c --- iptables-1.3.5.orig/ip6tables.c 2006-01-30 10:43:12.000000000 +0200 +++ iptables-1.3.5/ip6tables.c 2006-08-27 13:13:37.000000000 +0300 @@ -193,6 +193,8 @@ const char *program_name; char *lib_dir; +int kernel_version; + /* Keeping track of external matches and targets: linked lists. */ struct ip6tables_match *ip6tables_matches = NULL; struct ip6tables_target *ip6tables_targets = NULL; @@ -1047,10 +1049,51 @@ 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", @@ -1058,12 +1101,36 @@ 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); @@ -1700,6 +1767,14 @@ *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; @@ -1978,6 +2053,7 @@ 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); opts = merge_options(opts, m->extra_opts, &m->option_offset); @@ -2120,6 +2196,8 @@ 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);
pgpn0KncAjO1D.pgp
Description: PGP signature