+static void dump_target(struct connman_iptables *table,
+ struct ipt_entry *entry)
+
+{
+ struct xtables_target *xt_t;
+ struct xt_entry_target *target;
+
+ target = ipt_get_target(entry);
+
+ if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
+ struct xt_standard_target *t;
+
+ t = (struct xt_standard_target *)target;
+
+ switch (t->verdict) {
+ case XT_RETURN:
+ printf("\ttarget RETURN\n");
+ break;
+
+ case -NF_ACCEPT - 1:
+ printf("\ttarget ACCEPT\n");
+ break;
+
+ case -NF_DROP - 1:
+ printf("\ttarget DROP\n");
+ break;
+
+ case -NF_QUEUE - 1:
+ printf("\ttarget QUEUE\n");
+ break;
+
+ case -NF_STOP - 1:
+ printf("\ttarget STOP\n");
+ break;
+
+ default:
+ printf("\tJUMP @%p (0x%x)\n",
+ (char*)table->blob_entries->entrytable +
+ t->verdict, t->verdict);
+ break;
+ }
+
+ xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+ XTF_LOAD_MUST_SUCCEED);
+
+ if(xt_t->print != NULL)
+ xt_t->print(NULL, target, 1);
+ } else {
+ xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
+ if (xt_t == NULL) {
+ printf("\ttarget %s\n", target->u.user.name);
+ return;
+ }
+
+ if(xt_t->print != NULL) {
+ printf("\ttarget ");
+ xt_t->print(NULL, target, 1);
+ printf("\n");
+ }
+ }
+}
+
+static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
+{
+ struct xtables_match *xt_m;
+ struct xt_entry_match *match;
+
+ if (entry->elems == (unsigned char *)entry + entry->target_offset)
+ return;
+
+ match = (struct xt_entry_match *) entry->elems;
+
+ if (!strlen(match->u.user.name))
+ return;
+
+ xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
+ if (xt_m == NULL)
+ goto out;
+
+ if(xt_m->print != NULL) {
+ printf("\tmatch ");
+ xt_m->print(NULL, match, 1);
+ printf("\n");
+
+ return;
+ }
+
+out:
+ printf("\tmatch %s\n", match->u.user.name);
+
+}
+
+static int connman_iptables_dump_entry(struct ipt_entry *entry,
+ struct connman_iptables *table)
+{
+ struct xt_entry_target *target;
+ unsigned int offset;
+ int builtin;
+
+ offset = (char *)entry - (char *)table->blob_entries->entrytable;
+ target = ipt_get_target(entry);
+ builtin = is_hook_entry(table, entry);
+
+ if (entry_to_offset(table, entry) + entry->next_offset ==
+ table->blob_entries->size) {
+ printf("End of CHAIN 0x%x\n", offset);
+ return 0;
+ }
+
+ if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
+ printf("USER CHAIN (%s) %p match %p target %p size %d\n",
+ target->data, entry, entry->elems,
+ (char *)entry + entry->target_offset,
+ entry->next_offset);
+
+ return 0;
+ } else if (builtin >= 0) {
+ printf("CHAIN (%s) %p match %p target %p size %d\n",
+ hooknames[builtin], entry, entry->elems,
+ (char *)entry + entry->target_offset,
+ entry->next_offset);
+ } else {
+ printf("RULE %p match %p target %p size %d\n", entry,
+ entry->elems,
+ (char *)entry + entry->target_offset,
+ entry->next_offset);
+ }
+
+ dump_match(table, entry);
+ dump_target(table, entry);
+
+ return 0;
+}
+
+static void connman_iptables_dump_hook(struct connman_iptables *table)
+{
+ int i;
+ printf("hooks: \n");
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+ if ((table->info->valid_hooks & (1 << i)))
+ printf("%s entry %p underflow %p (%#x)\n",
+ hooknames[i],
+ table->blob_entries->entrytable +
+ table->info->hook_entry[i],
+ table->blob_entries->entrytable +
+ table->info->underflow[i],
+ table->info->underflow[i]);
+ }
+}
+
+static void connman_iptables_dump(struct connman_iptables *table)
+{
+ printf("%s valid_hooks=0x%08x, num_entries=%u, size=%u\n",
+ table->info->name,
+ table->info->valid_hooks, table->info->num_entries,
+ table->info->size);
+
+ connman_iptables_dump_hook(table);
+
+ ENTRY_ITERATE(table->blob_entries->entrytable,
+ table->blob_entries->size,
+ connman_iptables_dump_entry, table);
+
+}
+
+static int connman_iptables_get_entries(struct connman_iptables *table)
+{
+ socklen_t entry_size;
+
+ entry_size = sizeof(struct ipt_get_entries) + table->info->size;
+
+ return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
+ table->blob_entries, &entry_size);
+}
+
+static int connman_iptables_replace(struct connman_iptables *table,
+ struct ipt_replace *r)
+{
+ return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
+ sizeof(*r) + r->size);
+}
+
+static void connman_iptables_cleanup(struct connman_iptables *table)
+{
+ GList *list;
+ struct connman_iptables_entry *entry;
+
+ close(table->ipt_sock);
+
+ for (list = table->entries; list; list = list->next) {
+ entry = list->data;
+
+ g_free(entry->entry);
+ }
+
+ g_free(table->info);
+ g_free(table->blob_entries);
+ g_free(table);
+
+ xtables_free_opts(1);
+}
+
+static int connman_iptables_commit(struct connman_iptables *table)
+{
+ struct ipt_replace *repl;
+
+ repl = connman_iptables_blob(table);
+
+ return connman_iptables_replace(table, repl);
+}
+
+static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
+{
+ struct ipt_entry *new_entry;
+ int builtin;
+
+ new_entry = g_try_malloc0(entry->next_offset);
+ if (new_entry == NULL)
+ return -ENOMEM;
+
+ memcpy(new_entry, entry, entry->next_offset);
+
+ builtin = is_hook_entry(table, entry);
+
+ return connman_add_entry(table, new_entry, NULL, builtin);
+}
+
+static struct connman_iptables *connman_iptables_init(const char *table_name)
+{
+ 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;
+
+ table->info = g_try_new0(struct ipt_getinfo, 1);
+ if (table->info == NULL)
+ goto err;
+
+ table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
+ if (table->ipt_sock < 0)
+ goto err;
+
+ s = sizeof(*table->info);
+ strcpy(table->info->name, table_name);
+ if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
+ table->info, &s) < 0)
+ goto err;
+
+ table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
+ table->info->size);
+ if (table->blob_entries == NULL)
+ goto err;
+
+ strcpy(table->blob_entries->name, table_name);
+ table->blob_entries->size = table->info->size;
+
+ if (connman_iptables_get_entries(table) < 0)
+ goto err;
+
+ table->num_entries = 0;
+ table->old_entries = table->info->num_entries;
+ table->size = 0;
+
+ memcpy(table->underflow, table->info->underflow,
+ sizeof(table->info->underflow));
+ memcpy(table->hook_entry, table->info->hook_entry,
+ sizeof(table->info->hook_entry));
+
+ ENTRY_ITERATE(table->blob_entries->entrytable,
+ 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)
+{
+ struct xtables_target *xt_t = NULL;
+ gboolean is_builtin, is_user_defined;
+ GList *chain_head = NULL;
+ size_t target_size;
+
+ is_builtin = FALSE;
+ is_user_defined = FALSE;
+
+ if (is_builtin_target(target_name))
+ is_builtin = TRUE;
+ else {
+ chain_head = find_chain_head(table, target_name);
+ if (chain_head != NULL && chain_head->next != NULL)
+ is_user_defined = TRUE;
+ }
+
+ if (is_builtin || is_user_defined)
+ xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+ XTF_LOAD_MUST_SUCCEED);
+ else
+ xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
+
+ if (xt_t == NULL)
+ return NULL;
+
+ target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
+
+ xt_t->t = g_try_malloc0(target_size);
+ if (xt_t->t == NULL)
+ return NULL;
+
+ xt_t->t->u.target_size = target_size;
+
+ if (is_builtin || is_user_defined) {
+ struct xt_standard_target *target;
+
+ target = (struct xt_standard_target *)(xt_t->t);
+ strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
+
+ if (is_builtin == TRUE)
+ target->verdict = target_to_verdict(target_name);
+ else if (is_user_defined == TRUE) {
+ struct connman_iptables_entry *target_rule;
+
+ if (chain_head == NULL) {
+ g_free(xt_t->t);
+ return NULL;
+ }
+
+ target_rule = chain_head->next->data;
+ target->verdict = target_rule->offset;
+ }
+ } else {
+ strcpy(xt_t->t->u.user.name, target_name);
+ xt_t->t->u.user.revision = xt_t->revision;
+ if (xt_t->init != NULL)
+ xt_t->init(xt_t->t);
+ }
+
+ 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 xtables_match *prepare_matches(struct connman_iptables *table,
+ char *match_name)
+{
+ struct xtables_match *xt_m;
+ size_t match_size;
+
+ if (match_name == NULL)
+ return NULL;
+
+ xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, NULL);
+ 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) {
+ 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;
+ }
+ }
+
+ return xt_m;
+}