+ entry_target = ipt_get_target(new_entry);
+ memcpy(entry_target, xt_t->t, target_size);
+ }
+
+ return new_entry;
+}
+
+static void update_hooks(struct connman_iptables *table, GList *chain_head,
+ struct ipt_entry *entry)
+{
+ GList *list;
+ struct connman_iptables_entry *head, *e;
+ int builtin;
+
+ if (chain_head == NULL)
+ return;
+
+ head = chain_head->data;
+
+ builtin = head->builtin;
+ if (builtin < 0)
+ return;
+
+ table->underflow[builtin] += entry->next_offset;
+
+ for (list = chain_head->next; list; list = list->next) {
+ e = list->data;
+
+ builtin = e->builtin;
+ if (builtin < 0)
+ continue;
+
+ table->hook_entry[builtin] += entry->next_offset;
+ table->underflow[builtin] += entry->next_offset;
+ }
+}
+
+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,
+ int *builtin, struct xtables_rule_match *xt_rm)
+{
+ GList *chain_tail, *chain_head;
+ struct ipt_entry *new_entry;
+ struct connman_iptables_entry *head;
+
+ chain_head = find_chain_head(table, chain_name);
+ if (chain_head == NULL)
+ return NULL;
+
+ chain_tail = find_chain_tail(table, chain_name);
+ if (chain_tail == NULL)
+ return NULL;
+
+ new_entry = new_rule(ip, target_name, xt_t, xt_rm);
+ if (new_entry == NULL)
+ return NULL;
+
+ update_hooks(table, chain_head, new_entry);
+
+ /*
+ * 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 new_entry;
+}
+
+static int iptables_append_rule(struct connman_iptables *table,
+ struct ipt_ip *ip, char *chain_name,
+ char *target_name, struct xtables_target *xt_t,
+ struct xtables_rule_match *xt_rm)
+{
+ GList *chain_tail;
+ struct ipt_entry *new_entry;
+ int builtin = -1, ret;
+
+ DBG("");
+
+ chain_tail = find_chain_tail(table, chain_name);
+ if (chain_tail == NULL)
+ return -EINVAL;
+
+ new_entry = prepare_rule_inclusion(table, ip, chain_name,
+ target_name, xt_t, &builtin, xt_rm);
+ if (new_entry == NULL)
+ return -EINVAL;
+
+ ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin);
+ if (ret < 0)
+ g_free(new_entry);
+
+ return ret;
+}
+
+static int iptables_insert_rule(struct connman_iptables *table,
+ struct ipt_ip *ip, char *chain_name,
+ char *target_name, struct xtables_target *xt_t,
+ struct xtables_rule_match *xt_rm)
+{
+ struct ipt_entry *new_entry;
+ int builtin = -1, ret;
+ GList *chain_head;
+
+ chain_head = find_chain_head(table, chain_name);
+ if (chain_head == NULL)
+ return -EINVAL;
+
+ new_entry = prepare_rule_inclusion(table, ip, chain_name,
+ target_name, xt_t, &builtin, xt_rm);
+ if (new_entry == NULL)
+ return -EINVAL;
+
+ if (builtin == -1)
+ chain_head = chain_head->next;
+
+ ret = iptables_add_entry(table, new_entry, chain_head, builtin);
+ if (ret < 0)
+ g_free(new_entry);
+
+ return ret;
+}
+
+static gboolean is_same_ipt_entry(struct ipt_entry *i_e1,
+ struct ipt_entry *i_e2)
+{
+ if (memcmp(&i_e1->ip, &i_e2->ip, sizeof(struct ipt_ip)) != 0)
+ return FALSE;
+
+ if (i_e1->target_offset != i_e2->target_offset)
+ return FALSE;
+
+ if (i_e1->next_offset != i_e2->next_offset)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
+ struct xt_entry_target *xt_e_t2)
+{
+ if (xt_e_t1 == NULL || xt_e_t2 == NULL)
+ return FALSE;
+
+ if (strcmp(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
+ struct xt_standard_target *xt_s_t1;
+ struct xt_standard_target *xt_s_t2;
+
+ xt_s_t1 = (struct xt_standard_target *) xt_e_t1;
+ xt_s_t2 = (struct xt_standard_target *) xt_e_t2;
+
+ if (xt_s_t1->verdict != xt_s_t2->verdict)
+ return FALSE;
+ } else {
+ if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
+ return FALSE;
+
+ if (strcmp(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
+ struct xt_entry_match *xt_e_m2)
+{
+ if (xt_e_m1 == NULL || xt_e_m2 == NULL)
+ return FALSE;
+
+ if (xt_e_m1->u.match_size != xt_e_m2->u.match_size)
+ return FALSE;
+
+ if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
+ return FALSE;
+
+ if (strcmp(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static GList *find_existing_rule(struct connman_iptables *table,
+ struct ipt_ip *ip, char *chain_name,
+ char *target_name, struct xtables_target *xt_t,
+ 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;
+ struct xt_entry_match *xt_e_m = NULL;
+ struct connman_iptables_entry *entry;
+ struct ipt_entry *entry_test;
+ int builtin;
+
+ chain_head = find_chain_head(table, chain_name);
+ if (chain_head == NULL)
+ return NULL;
+
+ chain_tail = find_chain_tail(table, chain_name);
+ if (chain_tail == NULL)
+ return NULL;
+
+ if (!xt_t && !xt_m)
+ return NULL;
+
+ entry_test = new_rule(ip, target_name, xt_t, xt_rm);
+ if (entry_test == NULL)
+ return NULL;
+
+ if (xt_t != NULL)
+ xt_e_t = ipt_get_target(entry_test);
+ if (xt_m != NULL)
+ xt_e_m = (struct xt_entry_match *)entry_test->elems;
+
+ entry = chain_head->data;
+ builtin = entry->builtin;
+
+ if (builtin >= 0)
+ list = chain_head;
+ else
+ list = chain_head->next;
+
+ for (; list != chain_tail->prev; list = list->next) {
+ struct connman_iptables_entry *tmp;
+ struct ipt_entry *tmp_e;
+
+ tmp = list->data;
+ tmp_e = tmp->entry;
+
+ if (is_same_ipt_entry(entry_test, tmp_e) == FALSE)
+ continue;
+
+ if (xt_t != NULL) {
+ struct xt_entry_target *tmp_xt_e_t;
+
+ tmp_xt_e_t = ipt_get_target(tmp_e);
+
+ if (!is_same_target(tmp_xt_e_t, xt_e_t))
+ continue;
+ }
+
+ if (xt_m != NULL) {
+ struct xt_entry_match *tmp_xt_e_m;
+
+ tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems;
+
+ if (!is_same_match(tmp_xt_e_m, xt_e_m))
+ continue;
+ }
+
+ break;
+ }
+
+ g_free(entry_test);
+
+ if (list != chain_tail->prev)
+ return list;
+
+ return NULL;
+}
+
+static int iptables_delete_rule(struct connman_iptables *table,
+ struct ipt_ip *ip, char *chain_name,
+ char *target_name, struct xtables_target *xt_t,
+ struct xtables_match *xt_m,
+ struct xtables_rule_match *xt_rm)
+{
+ struct connman_iptables_entry *entry;
+ GList *chain_tail, *list;
+ int builtin, removed;
+
+ removed = 0;
+
+ chain_tail = find_chain_tail(table, chain_name);
+ if (chain_tail == NULL)
+ return -EINVAL;
+
+ list = find_existing_rule(table, ip, chain_name, target_name,
+ xt_t, xt_m, xt_rm);
+ if (list == NULL)
+ return -EINVAL;
+
+ entry = list->data;
+ if (entry == NULL)
+ return -EINVAL;
+
+ builtin = entry->builtin;
+
+ /* We have deleted a rule,
+ * all references should be bumped accordingly */
+ if (list->next != NULL)
+ update_targets_reference(table, list->next->data,
+ list->data, TRUE);
+
+ removed += remove_table_entry(table, entry);