iptables: Fix and refactor iterate_entries()
[platform/upstream/connman.git] / src / iptables.c
index 44c5c73..9e56d0b 100644 (file)
@@ -182,48 +182,65 @@ typedef int (*iterate_entries_cb_t)(struct ipt_entry *entry, int builtin,
                                        unsigned int hook,size_t size,
                                        unsigned int offset, void *user_data);
 
+static unsigned int next_hook_entry_index(unsigned int *valid_hooks)
+{
+       unsigned int h;
+
+       if (*valid_hooks == 0)
+               return NF_INET_NUMHOOKS;
+
+       h = __builtin_ffs(*valid_hooks) - 1;
+       *valid_hooks ^= (1 << h);
+
+       return h;
+}
+
 static int iterate_entries(struct ipt_entry *entries,
                                unsigned int valid_hooks,
                                unsigned int *hook_entry,
+                               unsigned int *underflow,
                                size_t size, iterate_entries_cb_t cb,
                                void *user_data)
 {
-       unsigned int i, h;
+       unsigned int offset, h, hook;
        int builtin, err;
        struct ipt_entry *entry;
 
-       if (valid_hooks != 0)
-               h = __builtin_ffs(valid_hooks) - 1;
-       else
-               h = NF_INET_NUMHOOKS;
+       h = next_hook_entry_index(&valid_hooks);
+       hook = h;
 
-       for (i = 0, entry = entries; i < size;
-                       i += entry->next_offset) {
+       for (offset = 0, entry = entries; offset < size;
+                       offset += entry->next_offset) {
                builtin = -1;
-               entry = (void *)entries + i;
+               entry = (void *)entries + offset;
 
                /*
-                * Find next valid hook which offset is higher
-                * or equal with the current offset.
+                * Updating builtin, hook and h is very tricky.
+                * The rules are:
+                * - builtin is only set to the current hook number
+                *   if the current entry is the hook entry (aka chain
+                *   head). And only for builtin chains, never for
+                *   the user chains.
+                * - hook is the current hook number. If we
+                *   look at user chains it needs to be NF_INET_NETNUMHOOKS.
+                * - h is the next hook entry. Thous we need to be carefully
+                *   not to access the table when h is NF_INET_NETNUMHOOKS.
                 */
-               if (h < NF_INET_NUMHOOKS) {
-                       if (hook_entry[h] < i) {
-                               valid_hooks ^= (1 << h);
-
-                               if (valid_hooks != 0)
-                                       h = __builtin_ffs(valid_hooks) - 1;
-                               else
-                                       h = NF_INET_NUMHOOKS;
-                       }
+               if (h < NF_INET_NUMHOOKS && hook_entry[h] == offset) {
+                       builtin = h;
+                       hook = h;
+               }
+
+               if (h == NF_INET_NUMHOOKS)
+                       hook = h;
 
-                       if (hook_entry[h] == i)
-                               builtin = h;
+               if (h < NF_INET_NUMHOOKS && underflow[h] <= offset) {
+                       h = next_hook_entry_index(&valid_hooks);
                }
 
-               err = cb(entry, builtin, h, size, i, user_data);
+               err = cb(entry, builtin, hook, size, offset, user_data);
                if (err < 0)
                        return err;
-
        }
 
        return 0;
@@ -301,9 +318,13 @@ static gboolean is_fallthrough(struct connman_iptables_entry *e)
        struct xt_entry_target *target;
 
        target = ipt_get_target(e->entry);
-       if (!strcmp(target->u.user.name, ""))
-               return true;
+       if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) {
+               struct xt_standard_target *t;
 
+               t = (struct xt_standard_target *)target;
+               if (t->verdict == 0)
+                       return true;
+       }
        return false;
 }
 
@@ -1269,6 +1290,7 @@ static void dump_table(struct connman_iptables *table)
        iterate_entries(table->blob_entries->entrytable,
                        table->info->valid_hooks,
                        table->info->hook_entry,
+                       table->info->underflow,
                        table->blob_entries->size,
                        print_entry, dump_entry);
 }
@@ -1293,7 +1315,8 @@ static void dump_ipt_replace(struct ipt_replace *repl)
                repl->underflow[NF_IP_POST_ROUTING]);
 
        iterate_entries(repl->entries, repl->valid_hooks,
-                       repl->hook_entry, repl->size, print_entry, dump_entry);
+                       repl->hook_entry, repl->underflow,
+                       repl->size, print_entry, dump_entry);
 }
 
 static int iptables_get_entries(struct connman_iptables *table)
@@ -1422,7 +1445,8 @@ static struct connman_iptables *iptables_init(const char *table_name)
 
        iterate_entries(table->blob_entries->entrytable,
                        table->info->valid_hooks, table->info->hook_entry,
-                       table->blob_entries->size, add_entry, table);
+                       table->info->underflow, table->blob_entries->size,
+                       add_entry, table);
 
        g_hash_table_insert(table_hash, g_strdup(table_name), table);
 
@@ -1653,24 +1677,6 @@ static struct connman_iptables *pre_load_table(const char *table_name,
        return iptables_init(table_name);
 }
 
-static void clear_tables_flags(void)
-{
-       struct xtables_match *xt_m;
-       struct xtables_target *xt_t;
-
-       /*
-        * Clear all flags because the flags are only valid
-        * for one rule.
-        */
-       for (xt_m = xtables_matches; xt_m != NULL; xt_m = xt_m->next)
-               xt_m->mflags = 0;
-
-       for (xt_t = xtables_targets; xt_t != NULL; xt_t = xt_t->next) {
-               xt_t->tflags = 0;
-               xt_t->used = 0;
-       }
-}
-
 struct parse_context {
        int argc;
        char **argv;
@@ -1687,7 +1693,7 @@ static int prepare_getopt_args(const char *str, struct parse_context *ctx)
 
        tokens = g_strsplit_set(str, " ", -1);
 
-       for (i = 0; tokens[i]; i++);
+       i = g_strv_length(tokens);
 
        /* Add space for the argv[0] value */
        ctx->argc = i + 1;
@@ -1707,6 +1713,8 @@ static int prepare_getopt_args(const char *str, struct parse_context *ctx)
        for (i = 1; i < ctx->argc; i++)
                ctx->argv[i] = tokens[i - 1];
 
+       g_free(tokens);
+
        return 0;
 }
 
@@ -1914,13 +1922,6 @@ static int parse_rule_spec(struct connman_iptables *table,
                return -ENOMEM;
 
        /*
-        * As side effect parsing a rule sets some global flags
-        * which will be evaluated/verified. Let's reset them
-        * to ensure we can parse more than one rule.
-        */
-       clear_tables_flags();
-
-       /*
         * Tell getopt_long not to generate error messages for unknown
         * options and also reset optind back to 0.
         */
@@ -2032,6 +2033,25 @@ out:
 
 static void reset_xtables(void)
 {
+       struct xtables_match *xt_m;
+       struct xtables_target *xt_t;
+
+       /*
+        * As side effect parsing a rule sets some global flags
+        * which will be evaluated/verified. Let's reset them
+        * to ensure we can parse more than one rule.
+        *
+        * Clear all flags because the flags are only valid
+        * for one rule.
+        */
+       for (xt_m = xtables_matches; xt_m != NULL; xt_m = xt_m->next)
+               xt_m->mflags = 0;
+
+       for (xt_t = xtables_targets; xt_t != NULL; xt_t = xt_t->next) {
+               xt_t->tflags = 0;
+               xt_t->used = 0;
+       }
+
        /*
         * We need also to free the memory implicitly allocated
         * during parsing (see xtables_options_xfrm()).
@@ -2046,6 +2066,8 @@ static void reset_xtables(void)
 
 static void cleanup_parse_context(struct parse_context *ctx)
 {
+       struct xtables_rule_match *rm, *tmp;
+
        g_strfreev(ctx->argv);
        g_free(ctx->ip);
        if (ctx->xt_t != NULL) {
@@ -2056,6 +2078,13 @@ static void cleanup_parse_context(struct parse_context *ctx)
                g_free(ctx->xt_m->m);
                ctx->xt_m->m = NULL;
        }
+       for (tmp = NULL, rm = ctx->xt_rm; rm != NULL; rm = rm->next) {
+               if (tmp != NULL)
+                       g_free(tmp);
+               tmp = rm;
+       }
+       g_free(tmp);
+
        g_free(ctx);
 }
 
@@ -2277,6 +2306,7 @@ void flush_table(const char *name)
        iterate_entries(table->blob_entries->entrytable,
                        table->info->valid_hooks,
                        table->info->hook_entry,
+                       table->info->underflow,
                        table->blob_entries->size,
                        flush_table_cb, &chains);