tools: find_chain_head/find_chain_tail code factorization in iptables-test
[framework/connectivity/connman.git] / tools / iptables-test.c
index ef5f9a4..555aa2a 100644 (file)
@@ -76,7 +76,7 @@ static const char *hooknames[] = {
 
 #define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1))
 
-struct ipt_error_target {
+struct error_target {
        struct xt_entry_target t;
        char error[IPT_TABLE_MAXNAMELEN];
 };
@@ -236,37 +236,15 @@ static GList *find_chain_tail(struct connman_iptables *table,
                                char *chain_name)
 {
        GList *chain_head, *list;
-       struct connman_iptables_entry *head, *tail;
-       struct ipt_entry *entry;
-       struct xt_entry_target *target;
-       int builtin;
-
-       /* First we look for the head */
-       for (list = table->entries; list; list = list->next) {
-               head = list->data;
-               entry = head->entry;
-
-               /* Buit-in chain */
-               builtin = head->builtin;
-               if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
-                       break;
+       struct connman_iptables_entry *tail;
 
-               /* User defined chain */
-               target = ipt_get_target(entry);
-               if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
-                   !strcmp((char *)target->data, chain_name))
-                       break;
-       }
-
-       if (list == NULL)
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
                return NULL;
 
-       chain_head = list;
-
        /* Then we look for the next chain */
        for (list = chain_head->next; list; list = list->next) {
                tail = list->data;
-               entry = tail->entry;
 
                if (is_chain(table, tail))
                        return list;
@@ -349,6 +327,71 @@ static int connman_add_entry(struct connman_iptables *table,
        return 0;
 }
 
+static int connman_iptables_flush_chain(struct connman_iptables *table,
+                                               char *name)
+{
+       GList *chain_head, *chain_tail, *list, *next;
+       struct connman_iptables_entry *entry;
+       int builtin, removed = 0;
+
+       chain_head = find_chain_head(table, name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+       builtin = entry->builtin;
+
+       if (builtin >= 0)
+               list = chain_head;
+       else
+               list = chain_head->next;
+
+       if (list == chain_tail->prev)
+               return 0;
+
+       while (list != chain_tail->prev) {
+               entry = list->data;
+               next = g_list_next(list);
+
+               table->num_entries--;
+               table->size -= entry->entry->next_offset;
+               removed += entry->entry->next_offset;
+
+               table->entries = g_list_remove(table->entries, list->data);
+
+               list = next;
+       }
+
+       if (builtin >= 0) {
+               struct connman_iptables_entry *e;
+
+               entry = list->data;
+
+               entry->builtin = builtin;
+
+               table->underflow[builtin] -= removed;
+
+               for (list = chain_tail; list; list = list->next) {
+                       e = list->data;
+
+                       builtin = e->builtin;
+                       if (builtin < 0)
+                               continue;
+
+                       table->hook_entry[builtin] -= removed;
+                       table->underflow[builtin] -= removed;
+               }
+       }
+
+       update_offsets(table);
+
+       return 0;
+}
+
 static int connman_iptables_delete_chain(struct connman_iptables *table,
                                                char *name)
 {
@@ -388,7 +431,7 @@ static int connman_iptables_add_chain(struct connman_iptables *table,
        GList *last;
        struct ipt_entry *entry_head;
        struct ipt_entry *entry_return;
-       struct ipt_error_target *error;
+       struct error_target *error;
        struct ipt_standard_target *standard;
        u_int16_t entry_head_size, entry_return_size;
 
@@ -405,23 +448,23 @@ static int connman_iptables_add_chain(struct connman_iptables *table,
 
        /* head entry */
        entry_head_size = sizeof(struct ipt_entry) +
-                               sizeof(struct ipt_error_target);
+                               sizeof(struct error_target);
        entry_head = g_try_malloc0(entry_head_size);
        if (entry_head == NULL)
-               goto err;
+               goto err_head;
 
        memset(entry_head, 0, entry_head_size);
 
        entry_head->target_offset = sizeof(struct ipt_entry);
        entry_head->next_offset = entry_head_size;
 
-       error = (struct ipt_error_target *) entry_head->elems;
+       error = (struct error_target *) entry_head->elems;
        strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
-       error->t.u.user.target_size = ALIGN(sizeof(struct ipt_error_target));
+       error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
        strcpy(error->error, name);
 
        if (connman_add_entry(table, entry_head, last, -1) < 0)
-               goto err;
+               goto err_head;
 
        /* tail entry */
        entry_return_size = sizeof(struct ipt_entry) +
@@ -446,8 +489,9 @@ static int connman_iptables_add_chain(struct connman_iptables *table,
        return 0;
 
 err:
-       g_free(entry_head);
        g_free(entry_return);
+err_head:
+       g_free(entry_head);
 
        return -ENOMEM;
 }
@@ -553,6 +597,7 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head, stru
                        continue;
 
                table->hook_entry[builtin] += entry->next_offset;
+               table->underflow[builtin] += entry->next_offset;
        }
 }
 
@@ -563,6 +608,8 @@ connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
 {
        GList *chain_tail, *chain_head;
        struct ipt_entry *new_entry;
+       struct connman_iptables_entry *head;
+       int builtin = -1;
 
        chain_head = find_chain_head(table, chain_name);
        if (chain_head == NULL)
@@ -580,7 +627,20 @@ connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
 
        update_hooks(table, chain_head, new_entry);
 
-       return connman_add_entry(table, new_entry, chain_tail, -1);
+       /*
+        * If the chain is builtin, and does not have any rule,
+        * then the one that we're inserting is becoming the head
+        * and thus needs the builtin flag.
+        */
+       head = chain_head->data;
+       if (head->builtin < 0)
+               builtin = -1;
+       else if (chain_head == chain_tail->prev) {
+               builtin = head->builtin;
+               head->builtin = -1;
+       }
+
+       return connman_add_entry(table, new_entry, chain_tail->prev, builtin);
 }
 
 static struct ipt_replace *
@@ -598,7 +658,7 @@ connman_iptables_blob(struct connman_iptables *table)
        memset(r, 0, sizeof(*r) + table->size);
 
        r->counters = g_try_malloc0(sizeof(struct xt_counters)
-                               * table->num_entries);
+                               * table->old_entries);
        if (r->counters == NULL) {
                g_free(r);
                return NULL;
@@ -765,11 +825,11 @@ static void connman_iptables_dump_hook(struct connman_iptables *table)
        printf("hooks: \n");
        for (i = 0; i < NF_INET_NUMHOOKS; i++) {
                if ((table->info->valid_hooks & (1 << i)))
-                       printf("%s entry 0x%x underflow 0x%x (0x%x)\n",
+                       printf("%s entry %p underflow %p (%#x)\n",
                                hooknames[i],
-                               (unsigned int)table->blob_entries->entrytable +
+                               table->blob_entries->entrytable +
                                                table->info->hook_entry[i],
-                               (unsigned int)table->blob_entries->entrytable +
+                               table->blob_entries->entrytable +
                                                table->info->underflow[i],
                                        table->info->underflow[i]);
        }
@@ -912,6 +972,7 @@ err:
 
 static struct option connman_iptables_opts[] = {
        {.name = "append",        .has_arg = 1, .val = 'A'},
+       {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
        {.name = "list",          .has_arg = 2, .val = 'L'},
        {.name = "new-chain",     .has_arg = 1, .val = 'N'},
        {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
@@ -935,7 +996,7 @@ int main(int argc, char *argv[])
        struct xtables_match *xt_m;
        struct xtables_target *xt_t;
        char *table_name, *chain, *new_chain, *match_name, *target_name;
-       char *delete_chain;
+       char *delete_chain, *flush_chain;
        int c;
        size_t size;
        gboolean dump, invert, delete;
@@ -946,18 +1007,22 @@ int main(int argc, char *argv[])
        invert = FALSE;
        delete = FALSE;
        table_name = chain = new_chain = match_name = target_name = NULL;
-       delete_chain = NULL;
+       delete_chain = flush_chain = NULL;
        table = NULL;
        xt_m = NULL;
        xt_t = NULL;
 
        while ((c = getopt_long(argc, argv,
-          "-A:L::N:X:j:i:m:o:t:", connman_iptables_globals.opts, NULL)) != -1) {
+          "-A:F:L::N:X:j:i:m:o:t:", connman_iptables_globals.opts, NULL)) != -1) {
                switch (c) {
                case 'A':
                        chain = optarg;
                        break;
 
+               case 'F':
+                       flush_chain = optarg;
+                       break;
+
                case 'L':
                        dump = true;
                        break;
@@ -989,7 +1054,11 @@ int main(int argc, char *argv[])
                        if (xt_t->init != NULL)
                                xt_t->init(xt_t->t);
                        connman_iptables_globals.opts =
-                               xtables_merge_options(connman_iptables_globals.opts,
+                               xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                                                    connman_iptables_globals.orig_opts,
+#endif
+                                                    connman_iptables_globals.opts,
                                                     xt_t->extra_opts,
                                                     &xt_t->option_offset);
                        if (connman_iptables_globals.opts == NULL)
@@ -1015,7 +1084,11 @@ int main(int argc, char *argv[])
                                xt_m->init(xt_m->m);
                        if (xt_m != xt_m->next) {
                                connman_iptables_globals.opts =
-                                       xtables_merge_options(connman_iptables_globals.opts,
+                                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                                               connman_iptables_globals.orig_opts,
+#endif
+                                               connman_iptables_globals.opts,
                                                xt_m->extra_opts,
                                                &xt_m->option_offset);
                                if (connman_iptables_globals.opts == NULL)
@@ -1078,6 +1151,14 @@ int main(int argc, char *argv[])
                goto commit;
        }
 
+       if (flush_chain) {
+               printf("Flush chain %s\n", flush_chain);
+
+               connman_iptables_flush_chain(table, flush_chain);
+
+               goto commit;
+       }
+
        if (dump) {
                connman_iptables_dump(table);