Hi Daniel,

On ti, 2015-07-28 at 08:57 +0200, Daniel Wagner wrote:
> 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.
> ---
>  src/connman.h         |   3 +
>  src/firewall.c        | 152 
> ++++++++++++++++++++++++++++++++++++++------------
>  tools/iptables-unit.c |  39 +++++++++++--
>  3 files changed, 152 insertions(+), 42 deletions(-)
> 
> diff --git a/src/connman.h b/src/connman.h
> index 29151af..654b8fa 100644
> --- a/src/connman.h
> +++ b/src/connman.h
> @@ -989,6 +989,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);
> diff --git a/src/firewall.c b/src/firewall.c
> index 90c3d3c..8f14c44 100644
> --- a/src/firewall.c
> +++ b/src/firewall.c
> @@ -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,51 @@ void __connman_firewall_destroy(struct firewall_context 
> *ctx)
>       g_free(ctx);
>  }
>  
> +static int firewall_enable_rule(struct fw_rule *rule)
> +{
> +     int err;
> +

Should we check if the rule is already enabled here?


> +     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 0;
> +
> +     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 +333,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)
> diff --git a/tools/iptables-unit.c b/tools/iptables-unit.c
> index 7e427e2..9c09867 100644
> --- a/tools/iptables-unit.c
> +++ b/tools/iptables-unit.c
> @@ -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();
>  


Cheers,
Jukka


_______________________________________________
connman mailing list
[email protected]
https://lists.connman.net/mailman/listinfo/connman

Reply via email to