Sorry about the corrupt patch. Here's a second try, hopefully without spurious line breaks. Michael --- include/xtables.h.in | 5 ++ ip6tables.c | 49 +++++++++----- iptables.c | 42 +++++++++---- xtables.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 30 deletions(-) diff --git a/include/xtables.h.in b/include/xtables.h.in index a399f90..a2b5f31 100644 --- a/include/xtables.h.in +++ b/include/xtables.h.in @@ -211,6 +211,7 @@ extern void xtables_init(void); extern void xtables_set_nfproto(uint8_t); extern void *xtables_calloc(size_t, size_t); extern void *xtables_malloc(size_t); +extern void *xtables_realloc(void*, size_t); extern int xtables_insmod(const char *, const char *, bool); extern int xtables_load_ko(const char *, bool); @@ -256,6 +257,8 @@ extern struct in_addr *xtables_numeric_to_ipaddr(const char *); extern struct in_addr *xtables_numeric_to_ipmask(const char *); extern void xtables_ipparse_any(const char *, struct in_addr **, struct in_addr *, unsigned int *); +extern void xtables_ipparse_multiple(const char *name, struct in_addr **addrpp, + struct in_addr **maskpp, unsigned int *naddrs); extern struct in6_addr *xtables_numeric_to_ip6addr(const char *); extern const char *xtables_ip6addr_to_numeric(const struct in6_addr *); @@ -263,6 +266,8 @@ extern const char *xtables_ip6addr_to_anyname(const struct in6_addr *); extern const char *xtables_ip6mask_to_numeric(const struct in6_addr *); extern void xtables_ip6parse_any(const char *, struct in6_addr **, struct in6_addr *, unsigned int *); +extern void xtables_ip6parse_multiple(const char *name, struct in6_addr **addrpp, + struct in6_addr **maskpp, unsigned int *naddrs); /** * Print the specified value to standard output, quoting dangerous diff --git a/ip6tables.c b/ip6tables.c index 35067f8..5433382 100644 --- a/ip6tables.c +++ b/ip6tables.c @@ -281,9 +281,9 @@ exit_printhelp(struct xtables_rule_match *matches) "Options:\n" "[!] --proto -p proto protocol: by number or name, eg. `tcp'\n" -"[!] --source -s address[/mask]\n" +"[!] --source -s address[/mask][,address[/mask]]*\n" " source specification\n" -"[!] --destination -d address[/mask]\n" +"[!] --destination -d address[/mask][,address[/mask]]*\n" " destination specification\n" "[!] --in-interface -i input name[+]\n" " network interface name ([+] for wildcard)\n" @@ -740,8 +740,10 @@ append_entry(const ip6t_chainlabel chain, struct ip6t_entry *fw, unsigned int nsaddrs, const struct in6_addr saddrs[], + const struct in6_addr smasks[], unsigned int ndaddrs, const struct in6_addr daddrs[], + const struct in6_addr dmasks[], int verbose, struct ip6tc_handle *handle) { @@ -750,8 +752,10 @@ append_entry(const ip6t_chainlabel chain, for (i = 0; i < nsaddrs; i++) { fw->ipv6.src = saddrs[i]; + fw->ipv6.smsk = smasks[i]; for (j = 0; j < ndaddrs; j++) { fw->ipv6.dst = daddrs[j]; + fw->ipv6.dmsk = dmasks[j]; if (verbose) print_firewall_line(fw, handle); ret &= ip6tc_append_entry(chain, fw, handle); @@ -784,8 +788,10 @@ insert_entry(const ip6t_chainlabel chain, unsigned int rulenum, unsigned int nsaddrs, const struct in6_addr saddrs[], + const struct in6_addr smasks[], unsigned int ndaddrs, const struct in6_addr daddrs[], + const struct in6_addr dmasks[], int verbose, struct ip6tc_handle *handle) { @@ -794,8 +800,10 @@ insert_entry(const ip6t_chainlabel chain, for (i = 0; i < nsaddrs; i++) { fw->ipv6.src = saddrs[i]; + fw->ipv6.smsk = smasks[i]; for (j = 0; j < ndaddrs; j++) { fw->ipv6.dst = daddrs[j]; + fw->ipv6.dmsk = dmasks[j]; if (verbose) print_firewall_line(fw, handle); ret &= ip6tc_insert_entry(chain, fw, rulenum, handle); @@ -806,7 +814,7 @@ insert_entry(const ip6t_chainlabel chain, } static unsigned char * -make_delete_mask(struct ip6t_entry *fw, struct xtables_rule_match *matches) +make_delete_mask(struct xtables_rule_match *matches) { /* Establish mask for comparison */ unsigned int size; @@ -843,8 +851,10 @@ delete_entry(const ip6t_chainlabel chain, struct ip6t_entry *fw, unsigned int nsaddrs, const struct in6_addr saddrs[], + const struct in6_addr smasks[], unsigned int ndaddrs, const struct in6_addr daddrs[], + const struct in6_addr dmasks[], int verbose, struct ip6tc_handle *handle, struct xtables_rule_match *matches) @@ -853,11 +863,13 @@ delete_entry(const ip6t_chainlabel chain, int ret = 1; unsigned char *mask; - mask = make_delete_mask(fw, matches); + mask = make_delete_mask(matches); for (i = 0; i < nsaddrs; i++) { fw->ipv6.src = saddrs[i]; + fw->ipv6.smsk = smasks[i]; for (j = 0; j < ndaddrs; j++) { fw->ipv6.dst = daddrs[j]; + fw->ipv6.dmsk = dmasks[j]; if (verbose) print_firewall_line(fw, handle); ret &= ip6tc_delete_entry(chain, fw, mask, handle); @@ -1291,9 +1303,9 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand int invert = 0; unsigned int nsaddrs = 0, ndaddrs = 0; struct in6_addr *saddrs = NULL, *daddrs = NULL; + struct in6_addr *smasks = NULL, *dmasks = NULL; int c, verbose = 0; - unsigned i; const char *chain = NULL; const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; const char *policy = NULL, *newname = NULL; @@ -1811,12 +1823,12 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand } if (shostnetworkmask) - xtables_ip6parse_any(shostnetworkmask, &saddrs, - &fw.ipv6.smsk, &nsaddrs); + xtables_ip6parse_multiple(shostnetworkmask, &saddrs, + &smasks, &nsaddrs); if (dhostnetworkmask) - xtables_ip6parse_any(dhostnetworkmask, &daddrs, - &fw.ipv6.dmsk, &ndaddrs); + xtables_ip6parse_multiple(dhostnetworkmask, &daddrs, + &dmasks, &ndaddrs); if ((nsaddrs > 1 || ndaddrs > 1) && (fw.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP))) @@ -1921,13 +1933,15 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand switch (command) { case CMD_APPEND: ret = append_entry(chain, e, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, *handle); break; case CMD_DELETE: ret = delete_entry(chain, e, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, *handle, matches); break; @@ -1941,7 +1955,8 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand break; case CMD_INSERT: ret = insert_entry(chain, e, rulenum - 1, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, *handle); break; @@ -2001,12 +2016,10 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand e = NULL; } - for (i = 0; i < nsaddrs; i++) - free(&saddrs[i]); - - for (i = 0; i < ndaddrs; i++) - free(&daddrs[i]); - + free(saddrs); + free(smasks); + free(daddrs); + free(dmasks); xtables_free_opts(1); return ret; diff --git a/iptables.c b/iptables.c index 649baf4..ccf265c 100644 --- a/iptables.c +++ b/iptables.c @@ -294,9 +294,9 @@ exit_printhelp(struct xtables_rule_match *matches) "Options:\n" "[!] --proto -p proto protocol: by number or name, eg. `tcp'\n" -"[!] --source -s address[/mask]\n" +"[!] --source -s address[/mask][,address[/mask]]*\n" " source specification\n" -"[!] --destination -d address[/mask]\n" +"[!] --destination -d address[/mask][,address[/mask]]*\n" " destination specification\n" "[!] --in-interface -i input name[+]\n" " network interface name ([+] for wildcard)\n" @@ -742,8 +742,10 @@ append_entry(const ipt_chainlabel chain, struct ipt_entry *fw, unsigned int nsaddrs, const struct in_addr saddrs[], + const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], + const struct in_addr dmasks[], int verbose, struct iptc_handle *handle) { @@ -752,8 +754,10 @@ append_entry(const ipt_chainlabel chain, for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; + fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; + fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) print_firewall_line(fw, handle); ret &= iptc_append_entry(chain, fw, handle); @@ -786,8 +790,10 @@ insert_entry(const ipt_chainlabel chain, unsigned int rulenum, unsigned int nsaddrs, const struct in_addr saddrs[], + const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], + const struct in_addr dmasks[], int verbose, struct iptc_handle *handle) { @@ -796,8 +802,10 @@ insert_entry(const ipt_chainlabel chain, for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; + fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; + fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) print_firewall_line(fw, handle); ret &= iptc_insert_entry(chain, fw, rulenum, handle); @@ -808,7 +816,7 @@ insert_entry(const ipt_chainlabel chain, } static unsigned char * -make_delete_mask(struct ipt_entry *fw, struct xtables_rule_match *matches) +make_delete_mask(struct xtables_rule_match *matches) { /* Establish mask for comparison */ unsigned int size; @@ -845,8 +853,10 @@ delete_entry(const ipt_chainlabel chain, struct ipt_entry *fw, unsigned int nsaddrs, const struct in_addr saddrs[], + const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], + const struct in_addr dmasks[], int verbose, struct iptc_handle *handle, struct xtables_rule_match *matches) @@ -855,11 +865,13 @@ delete_entry(const ipt_chainlabel chain, int ret = 1; unsigned char *mask; - mask = make_delete_mask(fw, matches); + mask = make_delete_mask(matches); for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; + fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; + fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) print_firewall_line(fw, handle); ret &= iptc_delete_entry(chain, fw, mask, handle); @@ -1313,7 +1325,8 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle struct ipt_entry fw, *e = NULL; int invert = 0; unsigned int nsaddrs = 0, ndaddrs = 0; - struct in_addr *saddrs = NULL, *daddrs = NULL; + struct in_addr *saddrs = NULL, *smasks = NULL; + struct in_addr *daddrs = NULL, *dmasks = NULL; int c, verbose = 0; const char *chain = NULL; @@ -1848,12 +1861,12 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle } if (shostnetworkmask) - xtables_ipparse_any(shostnetworkmask, &saddrs, - &fw.ip.smsk, &nsaddrs); + xtables_ipparse_multiple(shostnetworkmask, &saddrs, + &smasks, &nsaddrs); if (dhostnetworkmask) - xtables_ipparse_any(dhostnetworkmask, &daddrs, - &fw.ip.dmsk, &ndaddrs); + xtables_ipparse_multiple(dhostnetworkmask, &daddrs, + &dmasks, &ndaddrs); if ((nsaddrs > 1 || ndaddrs > 1) && (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) @@ -1961,13 +1974,15 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle switch (command) { case CMD_APPEND: ret = append_entry(chain, e, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, *handle); break; case CMD_DELETE: ret = delete_entry(chain, e, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, *handle, matches); break; @@ -1981,7 +1996,8 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle break; case CMD_INSERT: ret = insert_entry(chain, e, rulenum - 1, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, *handle); break; @@ -2042,7 +2058,9 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle } free(saddrs); + free(smasks); free(daddrs); + free(dmasks); xtables_free_opts(1); return ret; diff --git a/xtables.c b/xtables.c index a01d4ea..22ebf82 100644 --- a/xtables.c +++ b/xtables.c @@ -271,6 +271,18 @@ void *xtables_malloc(size_t size) return p; } +void *xtables_realloc(void* ptr, size_t size) +{ + void *p; + + if ((p = realloc(ptr, size)) == NULL) { + perror("ip[6]tables: realloc failed"); + exit(1); + } + + return p; +} + static char *get_modprobe(void) { int procfile; @@ -1133,6 +1145,93 @@ static struct in_addr *parse_ipmask(const char *mask) return &maskaddr; } +void +xtables_ipparse_multiple(const char *name, struct in_addr **addrpp, + struct in_addr **maskpp, unsigned int *naddrs) +{ + struct in_addr *addrp; + char buf[256]; + char *p; + int i, j; + unsigned int len, n; + + unsigned int count = 1; + const char* loop = name; + while( (loop = strchr(loop, (int)',')) != NULL ) { + ++count; + ++loop; /* skip ',' */ + } + + *addrpp = xtables_malloc(sizeof(struct in_addr) * count); + *maskpp = xtables_malloc(sizeof(struct in_addr) * count); + + loop = name; + + for( i = 0; i < count /*NB: count can grow*/; i++ ) { + if( !loop ) break; + if( ',' == *loop) ++loop; + if( '\0' == *loop ) break; + p = strchr(loop, (int)','); + if( p ) + len = p-loop; + else + len = strlen( loop ); + if( 0 == len || (sizeof(buf)-1) < len ) break; + + strncpy(buf, loop, len); + buf[len] = '\0'; + loop += len; + if ((p = strrchr(buf, '/')) != NULL) { + *p = '\0'; + addrp = parse_ipmask(p + 1); + } else + addrp = parse_ipmask(NULL); + memcpy(*maskpp+i, addrp, sizeof(*addrp)); + + /* if a null mask is given, the name is ignored, like in "any/0" */ + if ((*maskpp+i)->s_addr == 0L) + strcpy(buf, "0.0.0.0"); /* a bit pointless to + process multiple + addresses in this + case... */ + + addrp = ipparse_hostnetwork(buf, &n); + if( 1 < n ) { + count += n - 1; + *addrpp = xtables_realloc(*addrpp, + sizeof(struct in_addr) * count); + *maskpp = xtables_realloc(*maskpp, + sizeof(struct in_addr) * count); + for( j = 0; j < n; ++j ) /* for each new addr */ + memcpy(*addrpp+i+j, addrp+j, sizeof(*addrp)); + for( j = 1; j < n; ++j ) /* for each new mask */ + memcpy(*maskpp+i+j, *maskpp+i, sizeof(*addrp)); + i += n - 1; + } else { + memcpy(*addrpp+i, addrp, sizeof(*addrp)); + } + /* free what ipparse_hostnetwork had allocated: */ + free(addrp); + } + *naddrs = count; + for (i = 0, j = 0; i < n; i++) { + (*addrpp+i)->s_addr &= (*maskpp+i)->s_addr; +#if 0 + j++; + /* this is O(N^2) in the number of ip addresses. user should + provide unique ips */ + for (k = 0; k < j - 1; k++) { + if (addrp[k].s_addr == addrp[j - 1].s_addr) { + (*naddrs)--; + j--; + break; + } + } +#endif + } +} + + /** * xtables_ipparse_any - transform arbitrary name to in_addr * @@ -1364,6 +1463,84 @@ static struct in6_addr *parse_ip6mask(char *mask) return &maskaddr; } +void +xtables_ip6parse_multiple(const char *name, struct in6_addr **addrpp, + struct in6_addr **maskpp, unsigned int *naddrs) +{ + struct in6_addr *addrp; + char buf[256]; + char *p; + int i, j; + unsigned int len, n; + unsigned int count = 1; + const char* loop = name; + while( (loop = strchr(loop, (int)',')) != NULL ) { + ++count; + ++loop; /* skip ',' */ + } + + *addrpp = xtables_malloc(sizeof(struct in6_addr) * count); + *maskpp = xtables_malloc(sizeof(struct in6_addr) * count); + + loop = name; + + for( i = 0; i < count /*NB: count can grow*/; i++ ) { + if( !loop ) break; + if( ',' == *loop) ++loop; + if( '\0' == *loop ) break; + p = strchr(loop, (int)','); + if( p ) + len = p-loop; + else + len = strlen( loop ); + if( 0 == len || (sizeof(buf)-1) < len ) break; + + strncpy(buf, loop, len); + buf[len] = '\0'; + loop += len; + if ((p = strrchr(buf, '/')) != NULL) { + *p = '\0'; + addrp = parse_ip6mask(p + 1); + } else + addrp = parse_ip6mask(NULL); + memcpy((*maskpp)+i, addrp, sizeof(*addrp)); + + /* if a null mask is given, the name is ignored, like in "any/0" */ + if (!memcmp(*maskpp+i, &in6addr_any, sizeof(in6addr_any))) + strcpy(buf, "::"); + + addrp = ip6parse_hostnetwork(buf, &n); + /* ip6parse_hostnetwork only ever returns one IP + address (it exits if the resolution fails). + Therefore, n will always be 1 here. Leaving the + code below in anyway in case ip6parse_hostnetwork + is improved some day to behave like + ipparse_hostnetwork: */ + if( 1 < n ) { + count += n - 1; + *addrpp = xtables_realloc(*addrpp, + sizeof(struct in6_addr) * count); + *maskpp = xtables_realloc(*maskpp, + sizeof(struct in6_addr) * count); + for(j = 0; j < n; ++j) /* for each new addr */ + memcpy(*addrpp+i+j, addrp+j, sizeof(*addrp)); + for(j = 1; j < n; ++j) /* for each new mask */ + memcpy(*maskpp+i+j, *maskpp+i, sizeof(*addrp)); + i += n - 1; + } else { + memcpy(*addrpp+i, addrp, sizeof(*addrp)); + } + /* free what ip6parse_hostnetwork had allocated: */ + free(addrp); + } + *naddrs = count; + for (i = 0, j = 0; i < n; i++) { + int k; + for (k = 0; k < 4; k++) + (*addrpp+i)->s6_addr32[k] &= (*maskpp+i)->s6_addr32[k]; + } +} + void xtables_ip6parse_any(const char *name, struct in6_addr **addrpp, struct in6_addr *maskp, unsigned int *naddrs) { -- 1.5.6.3 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html