Commit 62b5f2fc authored by Daniel Wagner's avatar Daniel Wagner Committed by Pasi Sjöholm

firewall: Add support for single rule add/remove operations

We like to add and remove rules while the firewall is up and running.
For example we need to insert per Session rule in the global NAT table.
That could also be implemented destroying the whole table and recreate
it when need but that is quite an overhead.

Instead of taking down the whole table down we add an API to add and
remove new rules during runtime.
parent e9f9f541
......@@ -1001,6 +1001,9 @@ int __connman_firewall_add_rule(struct firewall_context *ctx,
const char *table,
const char *chain,
const char *rule_fmt, ...);
int __connman_firewall_remove_rule(struct firewall_context *ctx, int id);
int __connman_firewall_enable_rule(struct firewall_context *ctx, int id);
int __connman_firewall_disable_rule(struct firewall_context *ctx, int id);
int __connman_firewall_enable(struct firewall_context *ctx);
int __connman_firewall_disable(struct firewall_context *ctx);
bool __connman_firewall_is_up(void);
......
......@@ -2,7 +2,7 @@
*
* Connection Manager
*
* Copyright (C) 2013 BMW Car IT GmbH.
* Copyright (C) 2013,2015 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -31,6 +31,7 @@
#include "connman.h"
#define CHAIN_PREFIX "connman-"
#define FW_ALL_RULES -1
static const char *builtin_chains[] = {
[NF_IP_PRE_ROUTING] = "PREROUTING",
......@@ -46,6 +47,8 @@ struct connman_managed_table {
};
struct fw_rule {
int id;
bool enabled;
char *table;
char *chain;
char *rule_spec;
......@@ -58,6 +61,7 @@ struct firewall_context {
static GSList *managed_tables;
static bool firewall_is_up;
static unsigned int firewall_rule_id;
static int chain_to_index(const char *chain_name)
{
......@@ -267,6 +271,54 @@ void __connman_firewall_destroy(struct firewall_context *ctx)
g_free(ctx);
}
static int firewall_enable_rule(struct fw_rule *rule)
{
int err;
if (rule->enabled)
return -EALREADY;
DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec);
err = insert_managed_rule(rule->table, rule->chain, rule->rule_spec);
if (err < 0)
return err;
err = __connman_iptables_commit(rule->table);
if (err < 0)
return err;
rule->enabled = true;
return 0;
}
static int firewall_disable_rule(struct fw_rule *rule)
{
int err;
if (!rule->enabled)
return -EALREADY;
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));
return err;
}
err = __connman_iptables_commit(rule->table);
if (err < 0) {
connman_error("Cannot remove previously installed "
"iptables rules: %s", strerror(-err));
return err;
}
rule->enabled = false;
return 0;
}
int __connman_firewall_add_rule(struct firewall_context *ctx,
const char *table,
const char *chain,
......@@ -284,80 +336,109 @@ int __connman_firewall_add_rule(struct firewall_context *ctx,
rule = g_new0(struct fw_rule, 1);
rule->id = firewall_rule_id++;
rule->enabled = false;
rule->table = g_strdup(table);
rule->chain = g_strdup(chain);
rule->rule_spec = rule_spec;
ctx->rules = g_list_append(ctx->rules, rule);
return 0;
return rule->id;
}
static int firewall_disable(GList *rules)
int __connman_firewall_remove_rule(struct firewall_context *ctx, int id)
{
struct fw_rule *rule;
GList *list;
int err;
int err = -ENOENT;
for (list = rules; list; list = g_list_previous(list)) {
for (list = g_list_last(ctx->rules); list;
list = g_list_previous(list)) {
rule = list->data;
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));
return err;
if (rule->id == id || id == FW_ALL_RULES) {
ctx->rules = g_list_remove(ctx->rules, rule);
cleanup_fw_rule(rule);
err = 0;
if (id != FW_ALL_RULES)
break;
}
}
err = __connman_iptables_commit(rule->table);
if (err < 0) {
connman_error("Cannot remove previously installed "
"iptables rules: %s", strerror(-err));
return err;
return err;
}
int __connman_firewall_enable_rule(struct firewall_context *ctx, int id)
{
struct fw_rule *rule;
GList *list;
int err = -ENOENT;
for (list = g_list_first(ctx->rules); list; list = g_list_next(list)) {
rule = list->data;
if (rule->id == id || id == FW_ALL_RULES) {
err = firewall_enable_rule(rule);
if (err < 0)
break;
if (id != FW_ALL_RULES)
break;
}
}
return 0;
return err;
}
int __connman_firewall_enable(struct firewall_context *ctx)
int __connman_firewall_disable_rule(struct firewall_context *ctx, int id)
{
struct fw_rule *rule;
GList *list;
int err;
int e;
int err = -ENOENT;
for (list = g_list_first(ctx->rules); list;
list = g_list_next(list)) {
for (list = g_list_last(ctx->rules); list;
list = g_list_previous(list)) {
rule = list->data;
DBG("%s %s %s", rule->table, rule->chain, rule->rule_spec);
if (rule->id == id || id == FW_ALL_RULES) {
e = firewall_disable_rule(rule);
err = insert_managed_rule(rule->table,
rule->chain, rule->rule_spec);
if (err < 0)
goto err;
/* Report last error back */
if (e == 0 && err == -ENOENT)
err = 0;
else if (e < 0)
err = e;
err = __connman_iptables_commit(rule->table);
if (err < 0)
goto err;
if (id != FW_ALL_RULES)
break;
}
}
firewall_is_up = true;
return err;
}
return 0;
int __connman_firewall_enable(struct firewall_context *ctx)
{
int err;
err:
connman_warn("Failed to install iptables rules: %s", strerror(-err));
err = __connman_firewall_enable_rule(ctx, FW_ALL_RULES);
if (err < 0) {
connman_warn("Failed to install iptables rules: %s",
strerror(-err));
__connman_firewall_disable_rule(ctx, FW_ALL_RULES);
return err;
}
firewall_disable(g_list_previous(list));
firewall_is_up = true;
return err;
return 0;
}
int __connman_firewall_disable(struct firewall_context *ctx)
{
return firewall_disable(g_list_last(ctx->rules));
return __connman_firewall_disable_rule(ctx, FW_ALL_RULES);
}
bool __connman_firewall_is_up(void)
......
......@@ -24,6 +24,7 @@
#endif
#include <glib.h>
#include <errno.h>
#include "../src/connman.h"
......@@ -412,7 +413,7 @@ static void test_firewall_basic0(void)
err = __connman_firewall_add_rule(ctx, "filter", "INPUT",
"-m mark --mark 999 -j LOG");
g_assert(err == 0);
g_assert(err >= 0);
err = __connman_firewall_enable(ctx);
g_assert(err == 0);
......@@ -441,11 +442,11 @@ static void test_firewall_basic1(void)
err = __connman_firewall_add_rule(ctx, "filter", "INPUT",
"-m mark --mark 999 -j LOG");
g_assert(err == 0);
g_assert(err >= 0);
err = __connman_firewall_add_rule(ctx, "filter", "OUTPUT",
"-m mark --mark 999 -j LOG");
g_assert(err == 0);
g_assert(err >= 0);
err = __connman_firewall_enable(ctx);
g_assert(err == 0);
......@@ -466,11 +467,11 @@ static void test_firewall_basic2(void)
err = __connman_firewall_add_rule(ctx, "mangle", "INPUT",
"-j CONNMARK --restore-mark");
g_assert(err == 0);
g_assert(err >= 0);
err = __connman_firewall_add_rule(ctx, "mangle", "POSTROUTING",
"-j CONNMARK --save-mark");
g_assert(err == 0);
g_assert(err >= 0);
err = __connman_firewall_enable(ctx);
g_assert(err == 0);
......@@ -481,6 +482,33 @@ static void test_firewall_basic2(void)
__connman_firewall_destroy(ctx);
}
static void test_firewall_basic3(void)
{
struct firewall_context *ctx;
int err, id;
ctx = __connman_firewall_create();
g_assert(ctx);
id = __connman_firewall_add_rule(ctx, "mangle", "INPUT",
"-j CONNMARK --restore-mark");
g_assert(id >= 0);
err = __connman_firewall_enable_rule(ctx, id);
g_assert(err == 0);
err = __connman_firewall_disable_rule(ctx, id);
g_assert(err == 0);
err = __connman_firewall_remove_rule(ctx, id);
g_assert(err == 0);
err = __connman_firewall_disable(ctx);
g_assert(err == -ENOENT);
__connman_firewall_destroy(ctx);
}
static gchar *option_debug = NULL;
static bool parse_debug(const char *key, const char *value,
......@@ -543,6 +571,7 @@ int main(int argc, char *argv[])
g_test_add_func("/firewall/basic0", test_firewall_basic0);
g_test_add_func("/firewall/basic1", test_firewall_basic1);
g_test_add_func("/firewall/basic2", test_firewall_basic2);
g_test_add_func("/firewall/basic3", test_firewall_basic3);
err = g_test_run();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment