summaryrefslogtreecommitdiff
path: root/src/firewall.c
diff options
context:
space:
mode:
authorDaniel Wagner <daniel.wagner@bmw-carit.de>2013-03-19 13:46:33 +0100
committerPatrik Flykt <patrik.flykt@linux.intel.com>2013-03-25 13:17:58 +0200
commitea307271bf2ed3cb3f594fdbcd461d939b5565fb (patch)
tree9ba2120cb1d7565668f302f6a518b75b94ee1ec5 /src/firewall.c
parent8d9d64c7f2deda60e668bd74c09dfd4b16cfa0d2 (diff)
downloadconnman-ea307271bf2ed3cb3f594fdbcd461d939b5565fb.tar.gz
connman-ea307271bf2ed3cb3f594fdbcd461d939b5565fb.tar.bz2
connman-ea307271bf2ed3cb3f594fdbcd461d939b5565fb.zip
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.
Diffstat (limited to 'src/firewall.c')
-rw-r--r--src/firewall.c180
1 files changed, 174 insertions, 6 deletions
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 <config.h>
#endif
+#include <errno.h>
+
#include <xtables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
@@ -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);
}