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