X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=iptables.c;h=92c6c49cb777111e1d47826dbb9294f7c0063469;hb=6afea0b41dfbc3824956d11d960ad80097218feb;hp=5863ef483672c75c29a70c1c4d52d7b8e7637765;hpb=f7b70cf9e00324b89b02de213bcd0dde7044d035;p=iptables.git diff --git a/iptables.c b/iptables.c index 5863ef4..92c6c49 100644 --- a/iptables.c +++ b/iptables.c @@ -47,10 +47,6 @@ #define FALSE 0 #endif -#ifndef IPT_LIB_DIR -#define IPT_LIB_DIR "/usr/lib/iptables" -#endif - #ifndef PROC_SYS_MODPROBE #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" #endif @@ -105,7 +101,7 @@ static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', #define OPT_COUNTERS 0x00800U #define NUMBER_OF_OPT 12 static const char optflags[NUMBER_OF_OPT] -= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'}; += { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '0', 'c'}; static struct option original_opts[] = { { "append", 1, 0, 'A' }, @@ -147,14 +143,6 @@ static struct option original_opts[] = { * magic number of -1 */ int line = -1; -#ifndef __OPTIMIZE__ -struct ipt_entry_target * -ipt_get_target(struct ipt_entry *e) -{ - return (void *)e + e->target_offset; -} -#endif - static struct option *opts = original_opts; static unsigned int global_option_offset = 0; @@ -203,6 +191,7 @@ static int inverse_for_options[NUMBER_OF_OPT] = const char *program_version; const char *program_name; +char *lib_dir; /* Keeping track of external matches and targets: linked lists. */ struct iptables_match *iptables_matches = NULL; @@ -235,6 +224,7 @@ static const struct pprot chain_protos[] = { { "icmp", IPPROTO_ICMP }, { "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, + { "sctp", IPPROTO_SCTP }, { "all", 0 }, }; @@ -268,6 +258,7 @@ dotted_to_addr(const char *dotted) /* copy dotted string, because we need to modify it */ strncpy(buf, dotted, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; addrp = (unsigned char *) &(addr.s_addr); p = buf; @@ -315,6 +306,16 @@ inaddrcpy(struct in_addr *dst, struct in_addr *src) dst->s_addr = src->s_addr; } +static void free_opts(int reset_offset) +{ + if (opts != original_opts) { + free(opts); + opts = original_opts; + if (reset_offset) + global_option_offset = 0; + } +} + void exit_error(enum exittype status, char *msg, ...) { @@ -330,6 +331,8 @@ exit_error(enum exittype status, char *msg, ...) if (status == VERSION_PROBLEM) fprintf(stderr, "Perhaps iptables or your kernel needs to be upgraded.\n"); + /* On error paths, make sure that we don't leak memory */ + free_opts(1); exit(status); } @@ -340,13 +343,14 @@ exit_tryhelp(int status) fprintf(stderr, "Error occurred at line: %d\n", line); fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", program_name, program_name ); + free_opts(1); exit(status); } void -exit_printhelp(void) +exit_printhelp(struct iptables_rule_match *matches) { - struct iptables_match *m = NULL; + struct iptables_rule_match *matchp = NULL; struct iptables_target *t = NULL; printf("%s v%s\n\n" @@ -411,14 +415,16 @@ exit_printhelp(void) /* Print out any special helps. A user might like to be able to add a --help to the commandline, and see expected - results. So we call help for all matches & targets */ - for (t=iptables_targets;t;t=t->next) { - printf("\n"); - t->help(); + results. So we call help for all specified matches & targets */ + for (t = iptables_targets; t ;t = t->next) { + if (t->used) { + printf("\n"); + t->help(); + } } - for (m=iptables_matches;m;m=m->next) { + for (matchp = matches; matchp; matchp = matchp->next) { printf("\n"); - m->help(); + matchp->match->help(); } exit(0); } @@ -547,7 +553,7 @@ host_to_addr(const char *name, unsigned int *naddr) while (host->h_addr_list[*naddr] != (char *) NULL) (*naddr)++; - addr = fw_calloc(*naddr, sizeof(struct in_addr)); + addr = fw_calloc(*naddr, sizeof(struct in_addr) * *naddr); for (i = 0; i < *naddr; i++) inaddrcpy(&(addr[i]), (struct in_addr *) host->h_addr_list[i]); @@ -633,6 +639,7 @@ parse_hostnetworkmask(const char *name, struct in_addr **addrpp, int i, j, k, n; strncpy(buf, name, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; if ((p = strrchr(buf, '/')) != NULL) { *p = '\0'; addrp = parse_mask(p + 1); @@ -659,7 +666,7 @@ parse_hostnetworkmask(const char *name, struct in_addr **addrpp, } struct iptables_match * -find_match(const char *name, enum ipt_tryload tryload) +find_match(const char *name, enum ipt_tryload tryload, struct iptables_rule_match **matches) { struct iptables_match *ptr; @@ -670,13 +677,13 @@ find_match(const char *name, enum ipt_tryload tryload) #ifndef NO_SHARED_LIBS if (!ptr && tryload != DONT_LOAD) { - char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") + char path[strlen(lib_dir) + sizeof("/libipt_.so") + strlen(name)]; - sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); + sprintf(path, "%s/libipt_%s.so", lib_dir, name); if (dlopen(path, RTLD_NOW)) { /* Found library. If it didn't register itself, maybe they specified target as match. */ - ptr = find_match(name, DONT_LOAD); + ptr = find_match(name, DONT_LOAD, NULL); if (!ptr) exit_error(PARAMETER_PROBLEM, @@ -700,15 +707,24 @@ find_match(const char *name, enum ipt_tryload tryload) } #endif - if (ptr) - ptr->used = 1; + if (ptr && matches) { + struct iptables_rule_match **i; + struct iptables_rule_match *newentry; + + newentry = fw_malloc(sizeof(struct iptables_rule_match)); + + for (i = matches; *i; i = &(*i)->next); + newentry->match = ptr; + newentry->next = NULL; + *i = newentry; + } return ptr; } /* Christophe Burki wants `-p 6' to imply `-m tcp'. */ static struct iptables_match * -find_proto(const char *pname, enum ipt_tryload tryload, int nolookup) +find_proto(const char *pname, enum ipt_tryload tryload, int nolookup, struct iptables_rule_match **matches) { unsigned int proto; @@ -716,9 +732,9 @@ find_proto(const char *pname, enum ipt_tryload tryload, int nolookup) char *protoname = proto_to_name(proto, nolookup); if (protoname) - return find_match(protoname, tryload); + return find_match(protoname, tryload, matches); } else - return find_match(pname, tryload); + return find_match(pname, tryload, matches); return NULL; } @@ -753,8 +769,7 @@ parse_protocol(const char *s) return (u_int16_t)proto; } -static void -parse_interface(const char *arg, char *vianame, unsigned char *mask) +void parse_interface(const char *arg, char *vianame, unsigned char *mask) { int vialen = strlen(arg); unsigned int i; @@ -768,7 +783,7 @@ parse_interface(const char *arg, char *vianame, unsigned char *mask) " (%i)", arg, IFNAMSIZ-1); strcpy(vianame, arg); - if (vialen == 0) + if ((vialen == 0) || (vialen == 1 && vianame[0] == '+')) memset(mask, 0, IFNAMSIZ); else if (vianame[vialen - 1] == '+') { memset(mask, 0xFF, vialen - 1); @@ -815,8 +830,8 @@ parse_target(const char *targetname) if (strlen(targetname)+1 > sizeof(ipt_chainlabel)) exit_error(PARAMETER_PROBLEM, - "Invalid target name `%s' (%i chars max)", - targetname, sizeof(ipt_chainlabel)-1); + "Invalid target name `%s' (%u chars max)", + targetname, (unsigned int)sizeof(ipt_chainlabel)-1); for (ptr = targetname; *ptr; ptr++) if (isspace(*ptr)) @@ -886,18 +901,18 @@ mask_to_dotted(const struct in_addr *mask) } int -string_to_number(const char *s, unsigned int min, unsigned int max, - unsigned int *ret) +string_to_number_ll(const char *s, unsigned long long min, unsigned long long max, + unsigned long long *ret) { - long number; + unsigned long long number; char *end; /* Handle hex, octal, etc. */ errno = 0; - number = strtol(s, &end, 0); + number = strtoull(s, &end, 0); if (*end == '\0' && end != s) { /* we parsed a number, let's see if we want this */ - if (errno != ERANGE && min <= number && number <= max) { + if (errno != ERANGE && min <= number && (!max || number <= max)) { *ret = number; return 0; } @@ -905,6 +920,31 @@ string_to_number(const char *s, unsigned int min, unsigned int max, return -1; } +int +string_to_number_l(const char *s, unsigned long min, unsigned long max, + unsigned long *ret) +{ + int result; + unsigned long long number; + + result = string_to_number_ll(s, min, max, &number); + *ret = (unsigned long)number; + + return result; +} + +int string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + int result; + unsigned long number; + + result = string_to_number_l(s, min, max, &number); + *ret = (unsigned int)number; + + return result; +} + static void set_option(unsigned int *options, unsigned int option, u_int8_t *invflg, int invert) @@ -946,9 +986,9 @@ find_target(const char *name, enum ipt_tryload tryload) #ifndef NO_SHARED_LIBS if (!ptr && tryload != DONT_LOAD) { - char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") + char path[strlen(lib_dir) + sizeof("/libipt_.so") + strlen(name)]; - sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); + sprintf(path, "%s/libipt_%s.so", lib_dir, name); if (dlopen(path, RTLD_NOW)) { /* Found library. If it didn't register itself, maybe they specified match as a target. */ @@ -988,6 +1028,9 @@ merge_options(struct option *oldopts, const struct option *newopts, unsigned int num_old, num_new, i; struct option *merge; + /* Release previous options merged if any */ + free_opts(0); + for (num_old = 0; oldopts[num_old].name; num_old++); for (num_new = 0; newopts[num_new].name; num_new++); @@ -1005,10 +1048,56 @@ 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 ipt_get_revision rev; + socklen_t s = sizeof(rev); + int max_rev, sockfd; + + sockfd = socket(AF_INET, 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_IP, 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, IPT_SO_GET_REVISION_MATCH); +} + +static int compatible_target_revision(const char *name, u_int8_t revision) +{ + return compatible_revision(name, revision, IPT_SO_GET_REVISION_TARGET); +} + void register_match(struct iptables_match *me) { - struct iptables_match **i; + struct iptables_match **i, *old; if (strcmp(me->version, program_version) != 0) { fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n", @@ -1016,15 +1105,39 @@ register_match(struct iptables_match *me) exit(1); } - if (find_match(me->name, DONT_LOAD)) { - fprintf(stderr, "%s: match `%s' already registered.\n", + /* Revision field stole a char from name. */ + if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) { + fprintf(stderr, "%s: target `%s' has invalid name\n", program_name, me->name); exit(1); } + old = find_match(me->name, DONT_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 = &iptables_matches; *i!=old; i = &(*i)->next); + *i = old->next; + } + if (me->size != IPT_ALIGN(me->size)) { fprintf(stderr, "%s: match `%s' has invalid size %u.\n", - program_name, me->name, me->size); + program_name, me->name, (unsigned int)me->size); exit(1); } @@ -1040,21 +1153,49 @@ register_match(struct iptables_match *me) void register_target(struct iptables_target *me) { + struct iptables_target *old; + if (strcmp(me->version, program_version) != 0) { fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n", program_name, me->name, me->version, program_version); exit(1); } - if (find_target(me->name, DONT_LOAD)) { - fprintf(stderr, "%s: target `%s' already registered.\n", + /* Revision field stole a char from name. */ + if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) { + fprintf(stderr, "%s: target `%s' has invalid name\n", program_name, me->name); exit(1); } + old = find_target(me->name, DONT_LOAD); + if (old) { + struct iptables_target **i; + + if (old->revision == me->revision) { + fprintf(stderr, + "%s: target `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + /* Now we have two (or more) options, check compatibility. */ + if (compatible_target_revision(old->name, old->revision) + && old->revision > me->revision) + return; + + /* Replace if compatible. */ + if (!compatible_target_revision(me->name, me->revision)) + return; + + /* Delete old one. */ + for (i = &iptables_targets; *i!=old; i = &(*i)->next); + *i = old->next; + } + if (me->size != IPT_ALIGN(me->size)) { fprintf(stderr, "%s: target `%s' has invalid size %u.\n", - program_name, me->name, me->size); + program_name, me->name, (unsigned int)me->size); exit(1); } @@ -1077,17 +1218,17 @@ print_num(u_int64_t number, unsigned int format) number = (number + 500) / 1000; if (number > 9999) { number = (number + 500) / 1000; - printf(FMT("%4lluT ","%lluT "), number); + printf(FMT("%4lluT ","%lluT "), (unsigned long long)number); } - else printf(FMT("%4lluG ","%lluG "), number); + else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number); } - else printf(FMT("%4lluM ","%lluM "), number); + else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number); } else - printf(FMT("%4lluK ","%lluK "), number); + printf(FMT("%4lluK ","%lluK "), (unsigned long long)number); } else - printf(FMT("%5llu ","%llu "), number); + printf(FMT("%5llu ","%llu "), (unsigned long long)number); } else - printf(FMT("%8llu ","%llu "), number); + printf(FMT("%8llu ","%llu "), (unsigned long long)number); } @@ -1146,7 +1287,7 @@ print_match(const struct ipt_entry_match *m, const struct ipt_ip *ip, int numeric) { - struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD); + struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD, NULL); if (match) { if (match->print) @@ -1275,7 +1416,7 @@ print_firewall(const struct ipt_entry *fw, target->print(&fw->ip, t, format & FMT_NUMERIC); } else if (t->u.target_size != sizeof(*t)) printf("[%u bytes of unknown target data] ", - t->u.target_size - sizeof(*t)); + (unsigned int)(t->u.target_size - sizeof(*t))); if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); @@ -1362,20 +1503,16 @@ insert_entry(const ipt_chainlabel chain, } static unsigned char * -make_delete_mask(struct ipt_entry *fw) +make_delete_mask(struct ipt_entry *fw, struct iptables_rule_match *matches) { /* Establish mask for comparison */ unsigned int size; - struct iptables_match *m; + struct iptables_rule_match *matchp; unsigned char *mask, *mptr; size = sizeof(struct ipt_entry); - for (m = iptables_matches; m; m = m->next) { - if (!m->used) - continue; - - size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; - } + for (matchp = matches; matchp; matchp = matchp->next) + size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size; mask = fw_calloc(1, size + IPT_ALIGN(sizeof(struct ipt_entry_target)) @@ -1384,14 +1521,11 @@ make_delete_mask(struct ipt_entry *fw) memset(mask, 0xFF, sizeof(struct ipt_entry)); mptr = mask + sizeof(struct ipt_entry); - for (m = iptables_matches; m; m = m->next) { - if (!m->used) - continue; - + for (matchp = matches; matchp; matchp = matchp->next) { memset(mptr, 0xFF, IPT_ALIGN(sizeof(struct ipt_entry_match)) - + m->userspacesize); - mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + + matchp->match->userspacesize); + mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size; } memset(mptr, 0xFF, @@ -1409,13 +1543,14 @@ delete_entry(const ipt_chainlabel chain, unsigned int ndaddrs, const struct in_addr daddrs[], int verbose, - iptc_handle_t *handle) + iptc_handle_t *handle, + struct iptables_rule_match *matches) { unsigned int i, j; int ret = 1; unsigned char *mask; - mask = make_delete_mask(fw); + mask = make_delete_mask(fw, matches); for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; for (j = 0; j < ndaddrs; j++) { @@ -1425,6 +1560,8 @@ delete_entry(const ipt_chainlabel chain, ret &= iptc_delete_entry(chain, fw, mask, handle); } } + free(mask); + return ret; } @@ -1455,7 +1592,7 @@ for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), for (i = 0; i < chaincount; i++) { if (!builtinstoo && iptc_builtin(chains + i*sizeof(ipt_chainlabel), - *handle)) + *handle) == 1) continue; ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle); } @@ -1558,15 +1695,17 @@ static char *get_modprobe(void) int procfile; char *ret; +#define PROCFILE_BUFSIZ 1024 procfile = open(PROC_SYS_MODPROBE, O_RDONLY); if (procfile < 0) return NULL; - ret = malloc(1024); + ret = (char *) malloc(PROCFILE_BUFSIZ); if (ret) { - switch (read(procfile, ret, 1024)) { + memset(ret, 0, PROCFILE_BUFSIZ); + switch (read(procfile, ret, PROCFILE_BUFSIZ)) { case -1: goto fail; - case 1024: goto fail; /* Partial read. Wierd */ + case PROCFILE_BUFSIZ: goto fail; /* Partial read. Wierd */ } if (ret[strlen(ret)-1]=='\n') ret[strlen(ret)-1]=0; @@ -1583,7 +1722,7 @@ int iptables_insmod(const char *modname, const char *modprobe) { char *buf = NULL; char *argv[3]; - int i=0; + int status; /* If they don't explicitly set it, read out of kernel */ if (!modprobe) { @@ -1595,44 +1734,38 @@ int iptables_insmod(const char *modname, const char *modprobe) switch (fork()) { case 0: - /* close open file descriptors */ - for (i=0; i< 10; i++) { - close(i); - } argv[0] = (char *)modprobe; argv[1] = (char *)modname; argv[2] = NULL; execv(argv[0], argv); /* not usually reached */ - exit(0); + exit(1); case -1: return -1; default: /* parent */ - wait(NULL); + wait(&status); } free(buf); - return 0; + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + return 0; + return -1; } static struct ipt_entry * generate_entry(const struct ipt_entry *fw, - struct iptables_match *matches, + struct iptables_rule_match *matches, struct ipt_entry_target *target) { unsigned int size; - struct iptables_match *m; + struct iptables_rule_match *matchp; struct ipt_entry *e; size = sizeof(struct ipt_entry); - for (m = matches; m; m = m->next) { - if (!m->used) - continue; - - size += m->m->u.match_size; - } + for (matchp = matches; matchp; matchp = matchp->next) + size += matchp->match->m->u.match_size; e = fw_malloc(size + target->u.target_size); *e = *fw; @@ -1640,18 +1773,38 @@ generate_entry(const struct ipt_entry *fw, e->next_offset = size + target->u.target_size; size = 0; - for (m = matches; m; m = m->next) { - if (!m->used) - continue; - - memcpy(e->elems + size, m->m, m->m->u.match_size); - size += m->m->u.match_size; + for (matchp = matches; matchp; matchp = matchp->next) { + memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size); + size += matchp->match->m->u.match_size; } memcpy(e->elems + size, target, target->u.target_size); return e; } +void clear_rule_matches(struct iptables_rule_match **matches) +{ + struct iptables_rule_match *matchp, *tmp; + + for (matchp = *matches; matchp;) { + tmp = matchp->next; + if (matchp->match->m) + free(matchp->match->m); + free(matchp); + matchp = tmp; + } + + *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[IPT_FUNCTION_MAXNAMELEN - 2] = '\0'; + name[IPT_FUNCTION_MAXNAMELEN - 1] = revision; +} + int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) { struct ipt_entry fw, *e = NULL; @@ -1667,6 +1820,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) const char *pcnt = NULL, *bcnt = NULL; int ret = 1; struct iptables_match *m; + struct iptables_rule_match *matches = NULL; + struct iptables_rule_match *matchp; struct iptables_target *target = NULL; struct iptables_target *t; const char *jumpto = ""; @@ -1676,19 +1831,14 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) memset(&fw, 0, sizeof(fw)); - opts = original_opts; - global_option_offset = 0; - /* re-set optind to 0 in case do_command gets called * a second time */ optind = 0; /* clear mflags in case do_command gets called a second time * (we clear the global list of all matches for security)*/ - for (m = iptables_matches; m; m = m->next) { + for (m = iptables_matches; m; m = m->next) m->mflags = 0; - m->used = 0; - } for (t = iptables_targets; t; t = t->next) { t->tflags = 0; @@ -1774,10 +1924,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) break; case 'N': - if (optarg && *optarg == '-') + if (optarg && (*optarg == '-' || *optarg == '!')) exit_error(PARAMETER_PROBLEM, "chain name not allowed to start " - "with `-'\n"); + "with `%c'\n", *optarg); if (find_target(optarg, TRY_LOAD)) exit_error(PARAMETER_PROBLEM, "chain name may not clash " @@ -1828,10 +1978,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) optarg = argv[optind]; /* iptables -p icmp -h */ - if (!iptables_matches && protocol) - find_match(protocol, TRY_LOAD); + if (!matches && protocol) + find_match(protocol, TRY_LOAD, &matches); - exit_printhelp(); + exit_printhelp(matches); /* * Option selection @@ -1852,7 +2002,6 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) && (fw.ip.invflags & IPT_INV_PROTO)) exit_error(PARAMETER_PROBLEM, "rule would never match protocol"); - fw.nfcache |= NFC_IP_PROTO; break; case 's': @@ -1860,7 +2009,6 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) set_option(&options, OPT_SOURCE, &fw.ip.invflags, invert); shostnetworkmask = argv[optind-1]; - fw.nfcache |= NFC_IP_SRC; break; case 'd': @@ -1868,7 +2016,6 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) set_option(&options, OPT_DESTINATION, &fw.ip.invflags, invert); dhostnetworkmask = argv[optind-1]; - fw.nfcache |= NFC_IP_DST; break; case 'j': @@ -1887,7 +2034,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) target->t = fw_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); - target->init(target->t, &fw.nfcache); + set_revision(target->t->u.user.name, + target->revision); + if (target->init != NULL) + target->init(target->t, &fw.nfcache); opts = merge_options(opts, target->extra_opts, &target->option_offset); } break; @@ -1900,7 +2050,6 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) parse_interface(argv[optind-1], fw.ip.iniface, fw.ip.iniface_mask); - fw.nfcache |= NFC_IP_IF_IN; break; case 'o': @@ -1910,14 +2059,12 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) parse_interface(argv[optind-1], fw.ip.outiface, fw.ip.outiface_mask); - fw.nfcache |= NFC_IP_IF_OUT; break; case 'f': set_option(&options, OPT_FRAGMENT, &fw.ip.invflags, invert); fw.ip.flags |= IPT_F_FRAG; - fw.nfcache |= NFC_IP_FRAG; break; case 'v': @@ -1934,13 +2081,15 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --match"); - m = find_match(optarg, LOAD_MUST_SUCCEED); + m = find_match(optarg, LOAD_MUST_SUCCEED, &matches); size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; m->m = fw_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); - m->init(m->m, &fw.nfcache); + 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); } break; @@ -1992,12 +2141,12 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) "-%c requires packet and byte counter", opt2char(OPT_COUNTERS)); - if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1) + if (sscanf(pcnt, "%llu", (unsigned long long *)&fw.counters.pcnt) != 1) exit_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); - if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1) + if (sscanf(bcnt, "%llu", (unsigned long long *)&fw.counters.bcnt) != 1) exit_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(OPT_COUNTERS)); @@ -2026,18 +2175,16 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) argv, invert, &target->tflags, &fw, &target->t))) { - for (m = iptables_matches; m; m = m->next) { - if (!m->used) - continue; - - if (m->parse(c - m->option_offset, + for (matchp = matches; matchp; matchp = matchp->next) { + if (matchp->match->parse(c - matchp->match->option_offset, argv, invert, - &m->mflags, + &matchp->match->mflags, &fw, &fw.nfcache, - &m->m)) + &matchp->match->m)) break; } + m = matchp ? matchp->match : NULL; /* If you listen carefully, you can actually hear this code suck. */ @@ -2065,13 +2212,13 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) if (m == NULL && protocol && (!find_proto(protocol, DONT_LOAD, - options&OPT_NUMERIC) + options&OPT_NUMERIC, NULL) || (find_proto(protocol, DONT_LOAD, - options&OPT_NUMERIC) + options&OPT_NUMERIC, NULL) && (proto_used == 0)) ) && (m = find_proto(protocol, TRY_LOAD, - options&OPT_NUMERIC))) { + options&OPT_NUMERIC, &matches))) { /* Try loading protocol */ size_t size; @@ -2083,7 +2230,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) m->m = fw_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); - m->init(m->m, &fw.nfcache); + 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); @@ -2100,12 +2250,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) invert = FALSE; } - for (m = iptables_matches; m; m = m->next) { - if (!m->used) - continue; - - m->final_check(m->mflags); - } + for (matchp = matches; matchp; matchp = matchp->next) + matchp->match->final_check(matchp->match->mflags); if (target) target->final_check(target->tflags); @@ -2156,11 +2302,9 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) if (!*handle) *handle = iptc_init(*table); - if (!*handle) { - /* try to insmod the module if iptc_init failed */ - iptables_insmod("ip_tables", modprobe); + /* try to insmod the module if iptc_init failed */ + if (!*handle && iptables_insmod("ip_tables", modprobe) != -1) *handle = iptc_init(*table); - } if (!*handle) exit_error(VERSION_PROBLEM, @@ -2195,6 +2339,9 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) printf("Warning: using chain %s, not extension\n", jumpto); + if (target->t) + free(target->t); + target = NULL; } @@ -2213,7 +2360,9 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) target->t = fw_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); - target->init(target->t, &fw.nfcache); + set_revision(target->t->u.user.name, target->revision); + if (target->init != NULL) + target->init(target->t, &fw.nfcache); } if (!target) { @@ -2223,7 +2372,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) * chain. */ find_target(jumpto, LOAD_MUST_SUCCEED); } else { - e = generate_entry(&fw, iptables_matches, target->t); + e = generate_entry(&fw, matches, target->t); + free(target->t); } } @@ -2238,7 +2388,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) ret = delete_entry(chain, e, nsaddrs, saddrs, ndaddrs, daddrs, options&OPT_VERBOSE, - handle); + handle, matches); break; case CMD_DELETE_NUM: ret = iptc_delete_num_entry(chain, rulenum - 1, handle); @@ -2299,5 +2449,16 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) if (verbose > 1) dump_entries(*handle); + clear_rule_matches(&matches); + + if (e != NULL) { + free(e); + e = NULL; + } + + free(saddrs); + free(daddrs); + free_opts(1); + return ret; }