tools: Disable getopt unknown option display in iptables_test
[framework/connectivity/connman.git] / tools / iptables-test.c
index a7193c2..fb8cefe 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2011  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -46,6 +46,8 @@ static const char *hooknames[] = {
 #define LABEL_QUEUE   "QUEUE"
 #define LABEL_RETURN  "RETURN"
 
+#define XT_OPTION_OFFSET_SCALE 256
+
 /* fn returns 0 to continue iteration */
 #define _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
 ({                                                             \
@@ -55,7 +57,7 @@ static const char *hooknames[] = {
        type *__entry;                                          \
                                                                \
        for (__i = 0, __n = 0; __i < (size);                    \
-            __i += __entry->next_offset, __n++) {              \
+            __i += __entry->next_offset, __n++) {              \
                __entry = (void *)(entries) + __i;              \
                if (__n < n)                                    \
                        continue;                               \
@@ -529,18 +531,17 @@ err_head:
        return -ENOMEM;
 }
 
-static struct ipt_entry *
-new_rule(struct ipt_ip *ip, char *target_name,
-               struct xtables_target *xt_t,
-               char *match_name, struct xtables_match *xt_m)
+static struct ipt_entry *new_rule(struct ipt_ip *ip,
+                       char *target_name, struct xtables_target *xt_t,
+                       struct xtables_rule_match *xt_rm)
 {
+       struct xtables_rule_match *tmp_xt_rm;
        struct ipt_entry *new_entry;
        size_t match_size, target_size;
 
-       if (xt_m)
-               match_size = xt_m->m->u.match_size;
-       else
-               match_size = 0;
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next)
+               match_size += tmp_xt_rm->match->m->u.match_size;
 
        if (xt_t)
                target_size = ALIGN(xt_t->t->u.target_size);
@@ -557,11 +558,13 @@ new_rule(struct ipt_ip *ip, char *target_name,
        new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
        new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
                                                                match_size;
-       if (xt_m) {
-               struct xt_entry_match *entry_match;
 
-               entry_match = (struct xt_entry_match *)new_entry->elems;
-               memcpy(entry_match, xt_m->m, match_size);
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next) {
+               memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m,
+                                       tmp_xt_rm->match->m->u.match_size);
+               match_size += tmp_xt_rm->match->m->u.match_size;
        }
 
        if (xt_t) {
@@ -607,8 +610,7 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head,
 static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
                                struct ipt_ip *ip, char *chain_name,
                                char *target_name, struct xtables_target *xt_t,
-                               char *match_name, struct xtables_match *xt_m,
-                               int *builtin)
+                               int *builtin, struct xtables_rule_match *xt_rm)
 {
        GList *chain_tail, *chain_head;
        struct ipt_entry *new_entry;
@@ -622,7 +624,7 @@ static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
        if (chain_tail == NULL)
                return NULL;
 
-       new_entry = new_rule(ip, target_name, xt_t, match_name, xt_m);
+       new_entry = new_rule(ip, target_name, xt_t, xt_rm);
        if (new_entry == NULL)
                return NULL;
 
@@ -644,11 +646,10 @@ static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
        return new_entry;
 }
 
-static int
-connman_iptables_append_rule(struct connman_iptables *table,
+static int connman_iptables_append_rule(struct connman_iptables *table,
                                struct ipt_ip *ip, char *chain_name,
                                char *target_name, struct xtables_target *xt_t,
-                               char *match_name, struct xtables_match *xt_m)
+                               struct xtables_rule_match *xt_rm)
 {
        GList *chain_tail;
        struct ipt_entry *new_entry;
@@ -659,7 +660,7 @@ connman_iptables_append_rule(struct connman_iptables *table,
                return -EINVAL;
 
        new_entry = prepare_rule_inclusion(table, ip, chain_name,
-                       target_name, xt_t, match_name, xt_m, &builtin);
+                                       target_name, xt_t, &builtin, xt_rm);
        if (new_entry == NULL)
                return -EINVAL;
 
@@ -673,7 +674,7 @@ connman_iptables_append_rule(struct connman_iptables *table,
 static int connman_iptables_insert_rule(struct connman_iptables *table,
                                struct ipt_ip *ip, char *chain_name,
                                char *target_name, struct xtables_target *xt_t,
-                               char *match_name, struct xtables_match *xt_m)
+                               struct xtables_rule_match *xt_rm)
 {
        GList *chain_head;
        struct ipt_entry *new_entry;
@@ -684,7 +685,7 @@ static int connman_iptables_insert_rule(struct connman_iptables *table,
                return -EINVAL;
 
        new_entry = prepare_rule_inclusion(table, ip, chain_name,
-                       target_name, xt_t, match_name, xt_m, &builtin);
+                                       target_name, xt_t, &builtin, xt_rm);
        if (new_entry == NULL)
                return -EINVAL;
 
@@ -757,7 +758,8 @@ static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
 static int connman_iptables_delete_rule(struct connman_iptables *table,
                                struct ipt_ip *ip, char *chain_name,
                                char *target_name, struct xtables_target *xt_t,
-                               char *match_name, struct xtables_match *xt_m)
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
 {
        GList *chain_tail, *chain_head, *list;
        struct xt_entry_target *xt_e_t = NULL;
@@ -779,7 +781,7 @@ static int connman_iptables_delete_rule(struct connman_iptables *table,
        if (!xt_t && !xt_m)
                return -EINVAL;
 
-       entry_test = new_rule(ip, target_name, xt_t, match_name, xt_m);
+       entry_test = new_rule(ip, target_name, xt_t, xt_rm);
        if (entry_test == NULL)
                return -EINVAL;
 
@@ -866,8 +868,7 @@ static int connman_iptables_delete_rule(struct connman_iptables *table,
        return 0;
 }
 
-static struct ipt_replace *
-connman_iptables_blob(struct connman_iptables *table)
+static struct ipt_replace *connman_iptables_blob(struct connman_iptables *table)
 {
        struct ipt_replace *r;
        GList *list;
@@ -1137,9 +1138,23 @@ static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
 
 static struct connman_iptables *connman_iptables_init(const char *table_name)
 {
-       struct connman_iptables *table;
+       struct connman_iptables *table = NULL;
+       char *module = NULL;
        socklen_t s;
 
+       if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
+               goto err;
+
+       module = g_strconcat("iptable_", table_name, NULL);
+       if (module == NULL)
+               goto err;
+
+       if (xtables_insmod(module, NULL, TRUE) != 0)
+               goto err;
+
+       g_free(module);
+       module = NULL;
+
        table =  g_try_new0(struct connman_iptables, 1);
        if (table == NULL)
                return NULL;
@@ -1148,7 +1163,7 @@ static struct connman_iptables *connman_iptables_init(const char *table_name)
        if (table->info == NULL)
                goto err;
 
-       table->ipt_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
        if (table->ipt_sock < 0)
                goto err;
 
@@ -1182,16 +1197,40 @@ static struct connman_iptables *connman_iptables_init(const char *table_name)
                        table->blob_entries->size,
                                add_entry, table);
 
-
        return table;
 
 err:
+       g_free(module);
 
        connman_iptables_cleanup(table);
 
        return NULL;
 }
 
+static struct option connman_iptables_opts[] = {
+       {.name = "append",        .has_arg = 1, .val = 'A'},
+       {.name = "delete",        .has_arg = 1, .val = 'D'},
+       {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
+       {.name = "insert",        .has_arg = 1, .val = 'I'},
+       {.name = "list",          .has_arg = 2, .val = 'L'},
+       {.name = "new-chain",     .has_arg = 1, .val = 'N'},
+       {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
+       {.name = "destination",   .has_arg = 1, .val = 'd'},
+       {.name = "in-interface",  .has_arg = 1, .val = 'i'},
+       {.name = "jump",          .has_arg = 1, .val = 'j'},
+       {.name = "match",         .has_arg = 1, .val = 'm'},
+       {.name = "out-interface", .has_arg = 1, .val = 'o'},
+       {.name = "source",        .has_arg = 1, .val = 's'},
+       {.name = "table",         .has_arg = 1, .val = 't'},
+       {NULL},
+};
+
+struct xtables_globals connman_iptables_globals = {
+       .option_offset = 0,
+       .opts = connman_iptables_opts,
+       .orig_opts = connman_iptables_opts,
+};
+
 static struct xtables_target *prepare_target(struct connman_iptables *table,
                                                        char *target_name)
 {
@@ -1254,43 +1293,97 @@ static struct xtables_target *prepare_target(struct connman_iptables *table,
                        xt_t->init(xt_t->t);
        }
 
+       if (xt_t->x6_options != NULL)
+               connman_iptables_globals.opts =
+                       xtables_options_xfrm(
+#if XTABLES_VERSION_CODE > 5
+                               connman_iptables_globals.orig_opts,
+#endif
+                               connman_iptables_globals.opts,
+                               xt_t->x6_options,
+                               &xt_t->option_offset);
+       else
+               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) {
+               g_free(xt_t->t);
+               xt_t = NULL;
+       }
+
        return xt_t;
 }
 
-static struct option connman_iptables_opts[] = {
-       {.name = "append",        .has_arg = 1, .val = 'A'},
-       {.name = "delete",        .has_arg = 1, .val = 'D'},
-       {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
-       {.name = "insert",        .has_arg = 1, .val = 'I'},
-       {.name = "list",          .has_arg = 2, .val = 'L'},
-       {.name = "new-chain",     .has_arg = 1, .val = 'N'},
-       {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
-       {.name = "destination",   .has_arg = 1, .val = 'd'},
-       {.name = "in-interface",  .has_arg = 1, .val = 'i'},
-       {.name = "jump",          .has_arg = 1, .val = 'j'},
-       {.name = "match",         .has_arg = 1, .val = 'm'},
-       {.name = "out-interface", .has_arg = 1, .val = 'o'},
-       {.name = "source",        .has_arg = 1, .val = 's'},
-       {.name = "table",         .has_arg = 1, .val = 't'},
-       {NULL},
-};
+static struct xtables_match *prepare_matches(struct connman_iptables *table,
+                       struct xtables_rule_match **xt_rm, char *match_name)
+{
+       struct xtables_match *xt_m;
+       size_t match_size;
 
-struct xtables_globals connman_iptables_globals = {
-       .option_offset = 0,
-       .opts = connman_iptables_opts,
-       .orig_opts = connman_iptables_opts,
-};
+       if (match_name == NULL)
+               return NULL;
+
+       xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
+       match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
+
+       xt_m->m = g_try_malloc0(match_size);
+       if (xt_m->m == NULL)
+               return NULL;
+
+       xt_m->m->u.match_size = match_size;
+       strcpy(xt_m->m->u.user.name, xt_m->name);
+       xt_m->m->u.user.revision = xt_m->revision;
+
+       if (xt_m->init != NULL)
+               xt_m->init(xt_m->m);
+
+       if (xt_m == xt_m->next)
+               goto done;
+
+       if (xt_m->x6_options != NULL)
+               connman_iptables_globals.opts =
+                       xtables_options_xfrm(
+#if XTABLES_VERSION_CODE > 5
+                               connman_iptables_globals.orig_opts,
+#endif
+                               connman_iptables_globals.opts,
+                               xt_m->x6_options,
+                               &xt_m->option_offset);
+       else
+               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) {
+               g_free(xt_m->m);
+               xt_m = NULL;
+       }
+
+done:
+       return xt_m;
+}
 
 int main(int argc, char *argv[])
 {
        struct connman_iptables *table;
-       struct xtables_match *xt_m;
+       struct xtables_rule_match *xt_rm, *tmp_xt_rm;
+       struct xtables_match *xt_m, *xt_m_t;
        struct xtables_target *xt_t;
        struct ipt_ip ip;
        char *table_name, *chain, *new_chain, *match_name, *target_name;
        char *delete_chain, *flush_chain;
        int c, in_len, out_len;
-       size_t size;
        gboolean dump, invert, delete, insert, delete_rule;
        struct in_addr src, dst;
 
@@ -1305,9 +1398,13 @@ int main(int argc, char *argv[])
        delete_chain = flush_chain = NULL;
        memset(&ip, 0, sizeof(struct ipt_ip));
        table = NULL;
+       xt_rm = NULL;
        xt_m = NULL;
        xt_t = NULL;
 
+       /* extension's options will generate false-positives errors */
+       opterr = 0;
+
        while ((c = getopt_long(argc, argv, "-A:D:F:I:L::N:X:d:i:j:m:o:s:t:",
                                connman_iptables_globals.opts, NULL)) != -1) {
                switch (c) {
@@ -1382,33 +1479,17 @@ int main(int argc, char *argv[])
 
                case 'j':
                        target_name = optarg;
+                       xt_t = prepare_target(table, target_name);
+                       if (xt_t == NULL)
+                               goto out;
+
                        break;
 
                case 'm':
                        match_name = optarg;
-
-                       xt_m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, NULL);
-                       size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
-                       xt_m->m = g_try_malloc0(size);
+                       xt_m = prepare_matches(table, &xt_rm, match_name);
                        if (xt_m == NULL)
                                goto out;
-                       xt_m->m->u.match_size = size;
-                       strcpy(xt_m->m->u.user.name, xt_m->name);
-                       xt_m->m->u.user.revision = xt_m->revision;
-                       if (xt_m->init != NULL)
-                               xt_m->init(xt_m->m);
-                       if (xt_m != xt_m->next) {
-                               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)
-                                       goto out;
-                       }
 
                        break;
 
@@ -1457,20 +1538,48 @@ int main(int argc, char *argv[])
                        return -1;
 
                default:
-                       if (xt_t == NULL || xt_t->parse == NULL ||
-                           !xt_t->parse(c - xt_t->option_offset, argv, invert,
-                                       &xt_t->tflags, NULL, &xt_t->t)) {
-                               if (xt_m == NULL || xt_m->parse == NULL)
-                                       break;
-
-                               xt_m->parse(c - xt_m->option_offset, argv,
-                                       invert, &xt_m->mflags, NULL, &xt_m->m);
+                       if (xt_t != NULL && (xt_t->x6_parse != NULL ||
+                                               xt_t->parse != NULL) &&
+                                       (c >= (int) xt_t->option_offset &&
+                                       c < (int) xt_t->option_offset +
+                                       XT_OPTION_OFFSET_SCALE)) {
+                               xtables_option_tpcall(c, argv,
+                                                       invert, xt_t, NULL);
+
+                               break;
+                       }
+
+                       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                               xt_m_t = tmp_xt_rm->match;
+
+                               if (tmp_xt_rm->completed ||
+                                               (xt_m_t->x6_parse == NULL &&
+                                                xt_m_t->parse == NULL))
+                                       continue;
+
+                               if (c < (int) xt_m_t->option_offset ||
+                                       c >= (int) xt_m_t->option_offset
+                                       + XT_OPTION_OFFSET_SCALE)
+                                       continue;
+
+                               xtables_option_mpcall(c, argv,
+                                                       invert, xt_m_t, NULL);
+
+                               break;
                        }
 
                        break;
                }
        }
 
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               xtables_option_mfcall(tmp_xt_rm->match);
+
+       if (xt_t != NULL)
+               xtables_option_tfcall(xt_t);
+
        if (table_name == NULL)
                table_name = "filter";
 
@@ -1515,27 +1624,12 @@ int main(int argc, char *argv[])
        }
 
        if (chain) {
-               xt_t = prepare_target(table, target_name);
-               if (xt_t == NULL)
-                       goto out;
-
-               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)
-                       goto out;
-
                if (delete_rule == TRUE) {
                        printf("Deleting %s to %s (match %s)\n", target_name,
                                        chain, match_name);
 
                        connman_iptables_delete_rule(table, &ip, chain,
-                                       target_name, xt_t, match_name, xt_m);
+                                       target_name, xt_t, xt_m, xt_rm);
 
                        goto commit;
                }
@@ -1545,13 +1639,13 @@ int main(int argc, char *argv[])
                                        chain, match_name);
 
                        connman_iptables_insert_rule(table, &ip, chain,
-                                       target_name, xt_t, match_name, xt_m);
+                                               target_name, xt_t, xt_rm);
                } else {
                        printf("Appending %s to %s (match %s)\n", target_name,
                                        chain, match_name);
 
                        connman_iptables_append_rule(table, &ip, chain,
-                               target_name, xt_t, match_name, xt_m);
+                                               target_name, xt_t, xt_rm);
                }
        }