+ jmp_set = false;
+}
+
+static bool can_jmp()
+{
+ DBG("%s", jmp_set ? "true" : "false");
+ return jmp_set;
+}
+
+typedef int (*iterate_entries_cb_t)(struct connman_iptables_entry *entry,
+ int builtin, unsigned int hook,
+ size_t size, unsigned int offset,
+ void *user_data);
+
+static u_int16_t iptables_entry_get_next_offset(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return 0;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? entry->entry->next_offset : 0;
+ case AF_INET6:
+ return entry->entry6 ? entry->entry6->next_offset : 0;
+ }
+
+ return 0;
+}
+
+static u_int16_t iptables_entry_get_target_offset(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return 0;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? entry->entry->target_offset : 0;
+ case AF_INET6:
+ return entry->entry6 ? entry->entry6->target_offset : 0;
+ }
+
+ return 0;
+}
+
+static unsigned char *iptables_entry_get_elems(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return NULL;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? entry->entry->elems : NULL;
+ case AF_INET6:
+ return entry->entry6 ? entry->entry6->elems : NULL;
+ }
+
+ return NULL;
+}
+
+static struct xt_entry_target *iptables_entry_get_target(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return NULL;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? ipt_get_target(entry->entry) : NULL;
+ case AF_INET6:
+ return entry->entry6 ? ip6t_get_target(entry->entry6) : NULL;
+ }
+
+ return NULL;
+}
+
+static struct xt_counters *iptables_entry_get_counters(
+ struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return NULL;
+
+ switch (entry->type) {
+ case AF_INET:
+ return entry->entry ? &entry->entry->counters : NULL;
+ case AF_INET6:
+ return entry->entry6 ? &entry->entry6->counters : NULL;
+ }
+
+ return NULL;
+}
+
+static void iptables_entry_free(struct connman_iptables_entry *entry)
+{
+ if (!entry)
+ return;
+
+ g_free(entry->entry);
+ g_free(entry->entry6);
+ g_free(entry);
+}
+
+static const char *iptables_table_get_info_name(struct connman_iptables* table)
+{
+ if (!table)
+ return NULL;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->name;
+ case AF_INET6:
+ return table->info6->name;
+ }
+
+ return NULL;
+}
+
+static unsigned int iptables_table_get_info_num_entries(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return 0;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->num_entries;
+ case AF_INET6:
+ return table->info6->num_entries;
+ }
+
+ return 0;
+}
+
+static unsigned int iptables_table_get_info_size(struct connman_iptables* table)
+{
+ if (!table)
+ return 0;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->size;
+ case AF_INET6:
+ return table->info6->size;
+ }
+
+ return 0;
+}
+
+static unsigned int iptables_table_get_info_valid_hooks(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return 0;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->valid_hooks;
+ case AF_INET6:
+ return table->info6->valid_hooks;
+ }
+
+ return 0;
+}
+
+static unsigned int *iptables_table_get_info_hook_entry(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return NULL;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->hook_entry;
+ case AF_INET6:
+ return table->info6->hook_entry;
+ }
+
+ return NULL;
+}
+
+static unsigned int *iptables_table_get_info_underflow(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return NULL;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->info->underflow;
+ case AF_INET6:
+ return table->info6->underflow;
+ }
+
+ return NULL;
+}
+
+static unsigned int iptables_table_get_entries_size(
+ struct connman_iptables* table)
+{
+ if (!table)
+ return 0;
+
+ switch (table->type) {
+ case AF_INET:
+ return table->blob_entries->size;
+ case AF_INET6:
+ return table->blob_entries6->size;
+ }
+
+ return 0;
+}
+
+static const char *get_error_target(int type)
+{
+ switch (type) {
+ case AF_INET:
+ return IPT_ERROR_TARGET;
+ case AF_INET6:
+ return IP6T_ERROR_TARGET;
+ default:
+ return XT_ERROR_TARGET;
+ }
+}
+
+static const char *get_standard_target(int type)
+{
+ switch (type) {
+ case AF_INET:
+ return IPT_STANDARD_TARGET;
+ case AF_INET6:
+ return IP6T_STANDARD_TARGET;
+ default:
+ return XT_STANDARD_TARGET;
+ }
+}
+
+static struct connman_iptables *hash_table_lookup(int type,
+ const char *table_name) {
+
+ switch (type) {
+ case AF_INET:
+ return g_hash_table_lookup(table_hash, table_name);
+ case AF_INET6:
+ return g_hash_table_lookup(table_hash_ipv6, table_name);
+ }
+
+ return NULL;
+}
+
+static bool hash_table_replace(int type,
+ char *table_name,
+ struct connman_iptables *table) {
+
+ switch (type) {
+ case AF_INET:
+ return g_hash_table_replace(table_hash, table_name, table);
+ case AF_INET6:
+ return g_hash_table_replace(table_hash_ipv6, table_name, table);
+ }
+
+ return false;
+}
+
+static bool hash_table_remove(int type, const char *table_name)
+{
+ switch (type) {
+ case AF_INET:
+ return g_hash_table_remove(table_hash, table_name);
+ case AF_INET6:
+ return g_hash_table_remove(table_hash_ipv6, table_name);
+ }
+
+ return false;
+}
+
+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 connman_iptables_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 offset, h, hook;
+ int builtin, err;
+ struct connman_iptables_entry entry;
+
+ if (!entries)
+ return -EINVAL;
+
+ switch (entries->type) {
+ case AF_INET:
+ if (!entries->entry)
+ return -EINVAL;
+
+ break;
+ case AF_INET6:
+ if (!entries->entry6)
+ return -EINVAL;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ h = next_hook_entry_index(&valid_hooks);
+ hook = h;
+
+ entry.type = entries->type;
+ entry.entry = entries->entry;
+ entry.entry6 = entries->entry6;
+
+ for (offset = 0; offset < size;
+ offset += iptables_entry_get_next_offset(&entry)) {
+ builtin = -1;
+
+ switch (entries->type) {
+ case AF_INET:
+ entry.entry = (void* )entries->entry + offset;
+ break;
+ case AF_INET6:
+ entry.entry6 = (void* )entries->entry6 + offset;
+ break;
+ }
+
+ /*
+ * 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 && hook_entry[h] == offset) {
+ builtin = h;
+ hook = h;
+ }
+
+ if (h == NF_INET_NUMHOOKS)
+ hook = h;