From ea307271bf2ed3cb3f594fdbcd461d939b5565fb Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Tue, 19 Mar 2013 13:46:33 +0100 Subject: firewall: Maintain iptables rules in dedicated ConnMan chains Instead appending ConnMan iptables rules into the builtin chains we append them into chains managed by ConnMan. If a rule needs to be inserted into a bultin chain, ConnMan will create a 'connman-' prefixed builtin chain name and appends the user rules there. Then ConnMan will insert a unconditional jump rule in the builtin chain. Basically, iptables -t filter -A INPUT -m mark --mark 1 -j LOG will be translated to this: iptables -t filter -N connman-INPUT iptables -t filter -A connman-INPUT -m mark --mark 1 -j LOG iptables -t filter -I INPUT -j connman-INPUT When the last rule in a managed chain is removed, the managed chain will also be removed. --- src/firewall.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 6 deletions(-) (limited to 'src/firewall.c') diff --git a/src/firewall.c b/src/firewall.c index c44ddc12..c235d861 100644 --- a/src/firewall.c +++ b/src/firewall.c @@ -23,6 +23,8 @@ #include #endif +#include + #include #include @@ -38,6 +40,11 @@ static const char *builtin_chains[] = { [NF_IP_POST_ROUTING] = "POSTROUTING", }; +struct connman_managed_table { + char *name; + unsigned int chains[NF_INET_NUMHOOKS]; +}; + struct fw_rule { char *table; char *chain; @@ -48,6 +55,8 @@ struct firewall_context { GList *rules; }; +static GSList *managed_tables; + static int chain_to_index(const char *chain_name) { if (!g_strcmp0(builtin_chains[NF_IP_PRE_ROUTING], chain_name)) @@ -72,6 +81,165 @@ static int managed_chain_to_index(const char *chain_name) return chain_to_index(chain_name + strlen(CHAIN_PREFIX)); } +static int insert_managed_chain(const char *table_name, int id) +{ + char *rule, *managed_chain; + int err; + + managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, + builtin_chains[id]); + + err = __connman_iptables_new_chain(table_name, managed_chain); + if (err < 0) + goto out; + + rule = g_strdup_printf("-j %s", managed_chain); + err = __connman_iptables_insert(table_name, builtin_chains[id], rule); + g_free(rule); + if (err < 0) { + __connman_iptables_delete_chain(table_name, managed_chain); + goto out; + } + +out: + g_free(managed_chain); + + return err; +} + +static int delete_managed_chain(const char *table_name, int id) +{ + char *rule, *managed_chain; + int err; + + managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, + builtin_chains[id]); + + rule = g_strdup_printf("-j %s", managed_chain); + err = __connman_iptables_delete(table_name, builtin_chains[id], rule); + g_free(rule); + + if (err < 0) + goto out; + + err = __connman_iptables_delete_chain(table_name, managed_chain); + +out: + g_free(managed_chain); + + return err; +} + +static int insert_managed_rule(const char *table_name, + const char *chain_name, + const char *rule_spec) +{ + struct connman_managed_table *mtable = NULL; + GSList *list; + char *chain; + int id, err; + + id = chain_to_index(chain_name); + if (id < 0) { + /* This chain is not managed */ + chain = g_strdup(chain_name); + goto out; + } + + for (list = managed_tables; list != NULL; list = list->next) { + mtable = list->data; + + if (g_strcmp0(mtable->name, table_name) == 0) + break; + + mtable = NULL; + } + + if (mtable == NULL) { + mtable = g_new0(struct connman_managed_table, 1); + mtable->name = g_strdup(table_name); + + managed_tables = g_slist_prepend(managed_tables, mtable); + } + + if (mtable->chains[id] == 0) { + DBG("table %s add managed chain for %s", + table_name, chain_name); + + err = insert_managed_chain(table_name, id); + if (err < 0) + return err; + } + + mtable->chains[id]++; + chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name); + +out: + err = __connman_iptables_append(table_name, chain, rule_spec); + + g_free(chain); + + return err; + } + +static int delete_managed_rule(const char *table_name, + const char *chain_name, + const char *rule_spec) + { + struct connman_managed_table *mtable = NULL; + GSList *list; + int id, err; + char *managed_chain; + + id = chain_to_index(chain_name); + if (id < 0) { + /* This chain is not managed */ + return __connman_iptables_delete(table_name, chain_name, + rule_spec); + } + + managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name); + + err = __connman_iptables_delete(table_name, managed_chain, + rule_spec); + + for (list = managed_tables; list != NULL; list = list->next) { + mtable = list->data; + + if (g_strcmp0(mtable->name, table_name) == 0) + break; + + mtable = NULL; + } + + if (mtable == NULL) { + err = -ENOENT; + goto out; + } + + mtable->chains[id]--; + if (mtable->chains[id] > 0) + goto out; + + DBG("table %s remove managed chain for %s", + table_name, chain_name); + + err = delete_managed_chain(table_name, id); + + out: + g_free(managed_chain); + + return err; +} + +static void cleanup_managed_table(gpointer user_data) +{ + struct connman_managed_table *table = user_data; + + g_free(table->name); + g_free(table); +} + static void cleanup_fw_rule(gpointer user_data) { struct fw_rule *rule = user_data; @@ -132,9 +300,8 @@ static int firewall_disable(GList *rules) for (list = rules; list != NULL; list = g_list_previous(list)) { rule = list->data; - err = __connman_iptables_delete(rule->table, - rule->chain, - rule->rule_spec); + err = delete_managed_rule(rule->table, + rule->chain, rule->rule_spec); if (err < 0) { connman_error("Cannot remove previously installed " "iptables rules: %s", strerror(-err)); @@ -164,9 +331,8 @@ int __connman_firewall_enable(struct firewall_context *ctx) DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec); - err = __connman_iptables_append(rule->table, - rule->chain, - rule->rule_spec); + err = insert_managed_rule(rule->table, + rule->chain, rule->rule_spec); if (err < 0) goto err; @@ -270,4 +436,6 @@ int __connman_firewall_init(void) void __connman_firewall_cleanup(void) { DBG(""); + + g_slist_free_full(managed_tables, cleanup_managed_table); } -- cgit v1.2.3