From: Daniel Wagner <[email protected]>

---
 src/session.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 378 insertions(+), 14 deletions(-)

diff --git a/src/session.c b/src/session.c
index 82bfbc4..5e672e8 100644
--- a/src/session.c
+++ b/src/session.c
@@ -37,6 +37,8 @@ static GHashTable *session_hash;
 static connman_bool_t sessionmode;
 static struct connman_session *ecall_session;
 static GSList *policy_list;
+static uint32_t session_counter = 1234;
+static struct firewall_context *global_firewall;
 
 enum connman_session_trigger {
        CONNMAN_SESSION_TRIGGER_UNKNOWN         = 0,
@@ -93,6 +95,11 @@ struct connman_session {
 
        connman_bool_t ecall;
 
+       enum connman_session_id_type id_type;
+       struct nfacct_context *nfctx;
+       struct firewall_context *fw;
+       uint32_t mark;
+
        GSequence *service_list;
        GHashTable *service_hash;
 };
@@ -232,6 +239,178 @@ static char *service2bearer(enum connman_service_type 
type)
        return "";
 }
 
+static int init_firewall(void)
+{
+       struct firewall_context *fw;
+       int err;
+
+       fw = __connman_firewall_create();
+       if (fw == NULL)
+               return -ENOMEM;
+
+       err = __connman_firewall_add_rule(fw, "mangle", "INPUT",
+                                       "-j CONNMARK --restore-mark");
+       if (err < 0)
+               goto err;
+
+       err = __connman_firewall_add_rule(fw, "mangle", "POSTROUTING",
+                                       "-j CONNMARK --save-mark");
+       if (err < 0)
+               goto err;
+
+       err = __connman_firewall_enable(fw);
+       if (err < 0)
+               goto err;
+
+       global_firewall = fw;
+
+       return 0;
+
+err:
+       __connman_firewall_destroy(fw);
+
+       return err;
+}
+
+static void cleanup_firewall(void)
+{
+       if (global_firewall == NULL)
+               return;
+
+       __connman_firewall_disable(global_firewall);
+       __connman_firewall_destroy(global_firewall);
+}
+
+static int init_firewall_session(struct connman_session *session)
+{
+       struct firewall_context *fw;
+       int err;
+
+       DBG("");
+
+       fw = __connman_firewall_create();
+       if (fw == NULL)
+               return -ENOMEM;
+
+       switch (session->policy_config->id_type) {
+       case CONNMAN_SESSION_ID_TYPE_UID:
+               err = __connman_firewall_add_rule(fw, "mangle", "OUTPUT",
+                               "-m owner --uid-owner %s -j MARK --set-mark %d",
+                                               session->policy_config->id,
+                                               session->mark);
+               break;
+       case CONNMAN_SESSION_ID_TYPE_GID:
+               err = __connman_firewall_add_rule(fw, "mangle", "OUTPUT",
+                               "-m owner --gid-owner %s -j MARK --set-mark %d",
+                                               session->policy_config->id,
+                                               session->mark);
+               break;
+       case CONNMAN_SESSION_ID_TYPE_LSM:
+               err = __connman_firewall_add_rule(fw, "mangle", "OUTPUT",
+                               "-m secmark --selctx %s -j MARK --set-mark %d",
+                                               session->policy_config->id,
+                                               session->mark);
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       if (err < 0)
+               goto err;
+
+       session->id_type = session->policy_config->id_type;
+
+       err = __connman_firewall_add_rule(fw, "filter", "INPUT",
+               "-m mark --mark %d -m nfacct --nfacct-name session-input-%d",
+               session->mark,
+               session->mark);
+       if (err < 0)
+               goto err;
+
+       err = __connman_firewall_add_rule(fw, "filter", "OUTPUT",
+               "-m mark --mark %d -m nfacct --nfacct-name session-output-%d",
+               session->mark,
+               session->mark);
+       if (err < 0)
+               goto err;
+
+       err = __connman_firewall_enable(fw);
+       if (err)
+               goto err;
+
+       session->fw = fw;
+
+       return 0;
+
+err:
+       __connman_firewall_destroy(fw);
+
+       return err;
+}
+
+static void cleanup_firewall_session(struct connman_session *session)
+{
+       if (session->fw == NULL)
+               return;
+
+       __connman_firewall_disable(session->fw);
+       __connman_firewall_destroy(session->fw);
+
+       session->fw = NULL;
+}
+
+static int init_routing_table(struct connman_session *session)
+{
+       int err;
+
+       DBG("");
+
+       err = __connman_inet_add_fwmark_rule(session->mark,
+                                               AF_INET, session->mark);
+       if (err < 0)
+               return err;
+
+       err = __connman_inet_add_fwmark_rule(session->mark,
+                                               AF_INET6, session->mark);
+       if (err < 0)
+               __connman_inet_del_fwmark_rule(session->mark,
+                                               AF_INET, session->mark);
+
+       return err;
+}
+
+static void cleanup_routing_table(struct connman_session *session)
+{
+       DBG("");
+
+       __connman_inet_del_fwmark_rule(session->mark,
+                                       AF_INET6, session->mark);
+
+       __connman_inet_del_fwmark_rule(session->mark,
+                                       AF_INET, session->mark);
+}
+
+static void update_routing_table(struct connman_session *session)
+{
+       struct session_info *info = session->info;
+       struct connman_ipconfig *ipconfig;
+       const char *gateway;
+       int index;
+       int err;
+
+       if (info->entry == NULL)
+               return;
+
+       ipconfig = __connman_service_get_ip4config(info->entry->service);
+       index = __connman_ipconfig_get_index(ipconfig);
+       gateway = __connman_ipconfig_get_gateway(ipconfig);
+
+       DBG("index %d routing table %d default gateway %s", index, 
session->mark, gateway);
+       err = __connman_inet_add_default_to_table(session->mark, index, 
gateway);
+       if (err < 0)
+               DBG("session %p %s", strerror(-err));
+}
+
 static void destroy_policy_config(struct connman_session *session)
 {
        if (session->policy == NULL) {
@@ -261,16 +440,21 @@ static void free_session(struct connman_session *session)
        g_free(session);
 }
 
-static void cleanup_session(gpointer user_data)
+static void nfacct_cleanup_cb(int error, struct nfacct_context *ctx,
+                               void *user_data)
 {
        struct connman_session *session = user_data;
        struct session_info *info = session->info;
 
-       DBG("remove %s", session->session_path);
+       DBG("");
+
+       __connman_nfacct_destroy_context(session->nfctx);
 
        g_slist_free(session->user_allowed_bearers);
-       g_hash_table_destroy(session->service_hash);
-       g_sequence_free(session->service_list);
+       if (session->service_hash != NULL)
+               g_hash_table_destroy(session->service_hash);
+       if (session->service_list != NULL)
+               g_sequence_free(session->service_list);
 
        if (info->entry != NULL &&
                        info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
@@ -280,6 +464,24 @@ static void cleanup_session(gpointer user_data)
        free_session(session);
 }
 
+static void cleanup_session(gpointer user_data)
+{
+       struct connman_session *session = user_data;
+
+       DBG("remove %s", session->session_path);
+
+       cleanup_routing_table(session);
+       cleanup_firewall_session(session);
+       /*
+        * This is dangerous. If free_session is called from
+        * __connman_nfacct_disable_cb() in the error case
+        * the NFACCT code might access the context. At this
+        * point it works because we know that that is not
+        * the case... So here lies a dragon.
+        */
+       __connman_nfacct_disable(session->nfctx, nfacct_cleanup_cb, session);
+}
+
 static int assign_policy_plugin(struct connman_session *session)
 {
        if (session->policy != NULL)
@@ -297,6 +499,9 @@ struct creation_data {
        struct connman_session *session;
        DBusMessage *pending;
 
+       unsigned int acct_flags;
+       int acct_err;
+
        /* user config */
        enum connman_session_type type;
        GSList *allowed_bearers;
@@ -310,8 +515,6 @@ static void cleanup_creation_data(struct creation_data 
*creation_data)
        if (creation_data->pending != NULL)
                dbus_message_unref(creation_data->pending);
 
-       free_session(creation_data->session);
-
        g_slist_free(creation_data->allowed_bearers);
        g_free(creation_data);
 }
@@ -325,6 +528,7 @@ static int create_policy_config(struct connman_session 
*session,
        if (session->policy == NULL) {
                config = connman_session_create_default_config();
                if (config == NULL) {
+                       free_session(creation_data->session);
                        cleanup_creation_data(creation_data);
                        return -ENOMEM;
                }
@@ -1359,6 +1563,8 @@ static void session_changed(struct connman_session 
*session,
                 */
        case CONNMAN_SESSION_TRIGGER_CONNECT:
                if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
+                       update_routing_table(session);
+
                        if (info->entry->reason == 
CONNMAN_SESSION_REASON_CONNECT)
                                break;
                        info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
@@ -1383,6 +1589,7 @@ static void session_changed(struct connman_session 
*session,
                if (info->entry != NULL &&
                        (is_connecting(info->entry->state) == TRUE ||
                                is_connected(info->entry->state) == TRUE)) {
+                       update_routing_table(session);
                        break;
                }
 
@@ -1411,6 +1618,17 @@ int connman_session_config_update(struct connman_session 
*session)
         * might have changed. We can still optimize this later.
         */
 
+       if (session->id_type != session->policy_config->id_type) {
+               cleanup_firewall_session(session);
+               err = init_firewall_session(session);
+               if (err < 0) {
+                       connman_session_destroy(session);
+                       return err;
+               }
+
+               session->id_type = session->policy_config->id_type;
+       }
+
        err = apply_policy_on_bearers(
                session->policy_config->allowed_bearers,
                session->user_allowed_bearers,
@@ -1622,27 +1840,45 @@ static const GDBusMethodTable session_methods[] = {
        { },
 };
 
-static int session_config_cb(int err, struct connman_session *session,
-                               struct connman_session_config *config,
+static void session_create_failed_cb(int error,
+                               struct nfacct_context *ctx,
                                void *user_data)
 {
        struct creation_data *creation_data = user_data;
-       DBusMessage *reply;
+
+       DBG("error %d", error);
+
+       cleanup_session(creation_data->session);
+       cleanup_creation_data(creation_data);
+}
+
+static void session_create_final(struct creation_data *creation_data)
+{
+       struct connman_session *session = creation_data->session;
        struct session_info *info, *info_last;
+       DBusMessage *reply;
+       int err;
 
-       DBG("session %p config %p", session, config);
+       DBG("");
 
-       if (err != 0)
+       err = creation_data->acct_err;
+       if (err < 0)
                goto out;
 
-       session->policy_config = config;
+       err = init_firewall_session(session);
+       if (err < 0)
+               goto out;
 
-       info = session->info;
-       info_last = session->info_last;
+       err = init_routing_table(session);
+       if (err < 0)
+               goto out;
 
        if (session->policy_config->ecall == TRUE)
                ecall_session = session;
 
+       info = session->info;
+       info_last = session->info_last;
+
        info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
        info->config.type = apply_policy_on_type(
                                session->policy_config->type,
@@ -1696,11 +1932,108 @@ static int session_config_cb(int err, struct 
connman_session *session,
 
 out:
        if (err < 0) {
+               DBG("err %d", err);
                reply = __connman_error_failed(creation_data->pending, -err);
                g_dbus_send_message(connection, reply);
+               creation_data->pending = NULL;
+
+               __connman_nfacct_disable(session->nfctx,
+                                               session_create_failed_cb,
+                                               creation_data);
+               return;
        }
 
        cleanup_creation_data(creation_data);
+}
+
+static void session_nfacct_stats_cb(struct nfacct_context *ctx,
+                                       uint64_t packets, uint64_t bytes,
+                                       void *user_data)
+{
+       struct connman_session *session = user_data;
+
+       DBG("session %p", session);
+}
+
+static void session_nfacct_enable_cb(int error,
+                                       struct nfacct_context *ctx,
+                                       void *user_data)
+{
+       struct creation_data *creation_data = user_data;
+       DBusMessage *reply;
+
+       DBG("error %d", error);
+
+       if (error < 0)
+               goto err;
+
+       session_create_final(creation_data);
+       return;
+
+err:
+       reply = __connman_error_failed(creation_data->pending, -error);
+       g_dbus_send_message(connection, reply);
+       creation_data->pending = NULL;
+
+       cleanup_session(creation_data->session);
+       cleanup_creation_data(creation_data);
+}
+
+static int session_config_cb(int err, struct connman_session *session,
+                               struct connman_session_config *config,
+                               void *user_data)
+{
+       struct creation_data *creation_data = user_data;
+       char *input = NULL, *output = NULL;
+       DBusMessage *reply;
+
+       DBG("session %p config %p", session, config);
+
+       if (err < 0)
+               goto err;
+
+       session->policy_config = config;
+
+       session->mark = session_counter++;
+
+       session->nfctx = __connman_nfacct_create_context();
+       if (session->nfctx == NULL) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       input = g_strdup_printf("session-input-%d", session->mark);
+       err = __connman_nfacct_add(session->nfctx, input,
+                                       session_nfacct_stats_cb,
+                                       session);
+       if (err < 0)
+               goto err;
+
+       output = g_strdup_printf("session-output-%d", session->mark);
+       err = __connman_nfacct_add(session->nfctx, output,
+                                       session_nfacct_stats_cb,
+                                       session);
+       if (err < 0)
+               goto err;
+
+       err = __connman_nfacct_enable(session->nfctx,
+                                       session_nfacct_enable_cb,
+                                       creation_data);
+       if (err < 0)
+               goto err;
+
+       return -EINPROGRESS;
+
+err:
+       g_free(input);
+       g_free(output);
+
+       reply = __connman_error_failed(creation_data->pending, -err);
+       g_dbus_send_message(connection, reply);
+       creation_data->pending = NULL;
+
+       cleanup_session(creation_data->session);
+       cleanup_creation_data(creation_data);
 
        return err;
 }
@@ -1861,7 +2194,11 @@ int __connman_session_create(DBusMessage *msg)
 err:
        connman_error("Failed to create session");
 
+       if (creation_data->session != NULL)
+               cleanup_session(creation_data->session);
+
        cleanup_creation_data(creation_data);
+
        return err;
 }
 
@@ -2050,12 +2387,34 @@ static struct connman_notifier session_notifier = {
        .ipconfig_changed       = ipconfig_changed,
 };
 
+static int session_nfacct_flush_cb(int error, void *user_data)
+{
+       if (error == 0)
+               return 0;
+
+       connman_error("Could not flush nfacct table: %s", strerror(-error));
+
+       return 0;
+}
+
 int __connman_session_init(void)
 {
        int err;
 
        DBG("");
 
+       err = init_firewall();
+       if (err < 0) {
+               /*
+                * Since this might fail more often we add a warning
+                * to make debuggin simpler. If we get iptables
+                * under control we can get rid of this warning.
+                */
+               connman_warn("Could not initialize iptbles: %s",
+                               strerror(-err));
+               return err;
+       }
+
        connection = connman_dbus_get_connection();
        if (connection == NULL)
                return -1;
@@ -2063,6 +2422,7 @@ int __connman_session_init(void)
        err = connman_notifier_register(&session_notifier);
        if (err < 0) {
                dbus_connection_unref(connection);
+               cleanup_firewall();
                return err;
        }
 
@@ -2070,6 +2430,8 @@ int __connman_session_init(void)
                                                NULL, cleanup_session);
 
        sessionmode = FALSE;
+
+       __connman_nfacct_flush(session_nfacct_flush_cb, NULL);
        return 0;
 }
 
@@ -2080,6 +2442,8 @@ void __connman_session_cleanup(void)
        if (connection == NULL)
                return;
 
+       cleanup_firewall();
+
        connman_notifier_unregister(&session_notifier);
 
        g_hash_table_foreach(session_hash, release_session, NULL);
-- 
1.8.2.rc3.16.gce432ca

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

Reply via email to