summaryrefslogtreecommitdiff
path: root/src/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/session.c')
-rw-r--r--src/session.c427
1 files changed, 337 insertions, 90 deletions
diff --git a/src/session.c b/src/session.c
index 08facc1d..9e3c5594 100644
--- a/src/session.c
+++ b/src/session.c
@@ -37,13 +37,6 @@ static GHashTable *session_hash;
static GHashTable *service_hash;
static struct connman_session *ecall_session;
static uint32_t session_mark = 256;
-static struct firewall_context *global_firewall = NULL;
-
-enum connman_session_state {
- CONNMAN_SESSION_STATE_DISCONNECTED = 0,
- CONNMAN_SESSION_STATE_CONNECTED = 1,
- CONNMAN_SESSION_STATE_ONLINE = 2,
-};
struct session_info {
struct connman_session_config config;
@@ -64,6 +57,7 @@ struct connman_session {
struct connman_service *service_last;
struct connman_session_config *policy_config;
GSList *user_allowed_bearers;
+ char *user_allowed_interface;
bool ecall;
@@ -73,6 +67,7 @@ struct connman_session {
int index;
char *gateway;
bool policy_routing;
+ bool snat_enabled;
};
struct connman_service_info {
@@ -80,6 +75,15 @@ struct connman_service_info {
GSList *sessions;
};
+struct fw_snat {
+ GSList *sessions;
+ int id;
+ int index;
+ struct firewall_context *fw;
+};
+
+GSList *fw_snat_list;
+
static struct connman_session_policy *policy;
static void session_activate(struct connman_session *session);
static void session_deactivate(struct connman_session *session);
@@ -196,102 +200,109 @@ static char *service2bearer(enum connman_service_type type)
return "";
}
-static int init_firewall(void)
+static struct fw_snat *fw_snat_lookup(int index)
{
- struct firewall_context *fw;
- int err;
+ struct fw_snat *fw_snat;
+ GSList *list;
- if (global_firewall)
- return 0;
+ for (list = fw_snat_list; list; list = list->next) {
+ fw_snat = list->data;
- fw = __connman_firewall_create();
+ if (fw_snat->index == index)
+ return fw_snat;
+ }
+ return NULL;
+}
- err = __connman_firewall_add_rule(fw, "mangle", "INPUT",
- "-j CONNMARK --restore-mark");
- if (err < 0)
- goto err;
+static int fw_snat_create(struct connman_session *session,
+ int index, const char *ifname, const char *addr)
+{
+ struct fw_snat *fw_snat;
+ int err;
- err = __connman_firewall_add_rule(fw, "mangle", "POSTROUTING",
- "-j CONNMARK --save-mark");
- if (err < 0)
- goto err;
+ fw_snat = g_new0(struct fw_snat, 1);
- err = __connman_firewall_enable(fw);
- if (err < 0)
+ fw_snat->fw = __connman_firewall_create();
+ fw_snat->index = index;
+
+ fw_snat->id = __connman_firewall_enable_snat(fw_snat->fw,
+ index, ifname, addr);
+ if (fw_snat->id < 0) {
+ err = fw_snat->id;
goto err;
+ }
- global_firewall = fw;
+ fw_snat_list = g_slist_prepend(fw_snat_list, fw_snat);
+ fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session);
return 0;
-
err:
- __connman_firewall_destroy(fw);
-
+ __connman_firewall_destroy(fw_snat->fw);
+ g_free(fw_snat);
return err;
}
-static void cleanup_firewall(void)
+static void fw_snat_ref(struct connman_session *session,
+ struct fw_snat *fw_snat)
{
- if (!global_firewall)
+ if (g_slist_find(fw_snat->sessions, session))
return;
+ fw_snat->sessions = g_slist_prepend(fw_snat->sessions, session);
+}
- __connman_firewall_disable(global_firewall);
- __connman_firewall_destroy(global_firewall);
+static void fw_snat_unref(struct connman_session *session,
+ struct fw_snat *fw_snat)
+{
+ fw_snat->sessions = g_slist_remove(fw_snat->sessions, session);
+ if (fw_snat->sessions)
+ return;
+
+ fw_snat_list = g_slist_remove(fw_snat_list, fw_snat);
+
+ __connman_firewall_disable_snat(fw_snat->fw);
+ __connman_firewall_destroy(fw_snat->fw);
+ g_free(fw_snat);
}
static int init_firewall_session(struct connman_session *session)
{
struct firewall_context *fw;
int err;
+ struct connman_ipconfig *ipconfig = NULL;
+ const char *addr = NULL;
- if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN)
+ if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN &&
+ !session->info->config.source_ip_rule)
return 0;
DBG("");
- err = init_firewall();
- if (err < 0)
- return err;
+ if (session->info->config.source_ip_rule) {
+ ipconfig = __connman_service_get_ip4config(session->service);
+ if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN && !ipconfig)
+ return 0;
+ }
fw = __connman_firewall_create();
if (!fw)
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:
- default:
- err = -EINVAL;
+ if (session->info->config.source_ip_rule && ipconfig) {
+ addr = __connman_ipconfig_get_local(ipconfig);
}
- if (err < 0)
- goto err;
-
+ err =__connman_firewall_enable_marking(fw,
+ session->policy_config->id_type,
+ session->policy_config->id,
+ addr, session->mark);
+ if (err < 0) {
+ __connman_firewall_destroy(fw);
+ return err;
+ }
session->id_type = session->policy_config->id_type;
-
- 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)
@@ -299,7 +310,8 @@ static void cleanup_firewall_session(struct connman_session *session)
if (!session->fw)
return;
- __connman_firewall_disable(session->fw);
+ __connman_firewall_disable_marking(session->fw);
+ __connman_firewall_disable_snat(session->fw);
__connman_firewall_destroy(session->fw);
session->fw = NULL;
@@ -309,7 +321,11 @@ static int init_routing_table(struct connman_session *session)
{
int err;
- if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN)
+ if (session->policy_config->id_type == CONNMAN_SESSION_ID_TYPE_UNKNOWN &&
+ !session->info->config.source_ip_rule)
+ return 0;
+
+ if (!session->service)
return 0;
DBG("");
@@ -348,6 +364,7 @@ static void add_default_route(struct connman_session *session)
{
struct connman_ipconfig *ipconfig;
int err;
+ struct in_addr addr = { INADDR_ANY };
if (!session->service)
return;
@@ -356,6 +373,9 @@ static void add_default_route(struct connman_session *session)
session->index = __connman_ipconfig_get_index(ipconfig);
session->gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig));
+ if (!session->gateway)
+ session->gateway = g_strdup(inet_ntoa(addr));
+
DBG("index %d routing table %d default gateway %s",
session->index, session->mark, session->gateway);
@@ -365,6 +385,62 @@ static void add_default_route(struct connman_session *session)
DBG("session %p %s", session, strerror(-err));
}
+static void del_nat_rules(struct connman_session *session)
+{
+ struct fw_snat *fw_snat;
+
+ if (!session->snat_enabled)
+ return;
+
+ session->snat_enabled = false;
+ fw_snat = fw_snat_lookup(session->index);
+
+ if (!fw_snat)
+ return;
+
+ fw_snat_unref(session, fw_snat);
+}
+
+static void add_nat_rules(struct connman_session *session)
+{
+ struct connman_ipconfig *ipconfig;
+ struct fw_snat *fw_snat;
+ const char *addr;
+ int index, err;
+ char *ifname;
+
+ if (!session->service)
+ return;
+
+ ipconfig = __connman_service_get_ip4config(session->service);
+ index = __connman_ipconfig_get_index(ipconfig);
+ ifname = connman_inet_ifname(index);
+ addr = __connman_ipconfig_get_local(ipconfig);
+
+ if (!addr)
+ return;
+
+ session->snat_enabled = true;
+ fw_snat = fw_snat_lookup(index);
+ if (fw_snat) {
+ fw_snat_ref(session, fw_snat);
+ return;
+ }
+
+ err = fw_snat_create(session, index, ifname, addr);
+ if (err < 0) {
+ DBG("failed to add SNAT rule");
+ session->snat_enabled = false;
+ }
+
+ g_free(ifname);
+}
+
+uint32_t connman_session_firewall_get_fwmark(struct connman_session *session)
+{
+ return session->mark;
+}
+
static void cleanup_routing_table(struct connman_session *session)
{
DBG("");
@@ -381,12 +457,24 @@ static void cleanup_routing_table(struct connman_session *session)
del_default_route(session);
}
+static void update_firewall(struct connman_session *session)
+{
+ cleanup_firewall_session(session);
+ init_firewall_session(session);
+}
+
static void update_routing_table(struct connman_session *session)
{
- del_default_route(session);
+ cleanup_routing_table(session);
+ init_routing_table(session);
add_default_route(session);
}
+static void cleanup_nat_rules(struct connman_session *session)
+{
+ del_nat_rules(session);
+}
+
static void destroy_policy_config(struct connman_session *session)
{
if (!policy) {
@@ -407,6 +495,7 @@ static void free_session(struct connman_session *session)
destroy_policy_config(session);
g_slist_free(session->info->config.allowed_bearers);
+ g_free(session->info->config.allowed_interface);
g_free(session->owner);
g_free(session->session_path);
g_free(session->notify_path);
@@ -434,6 +523,7 @@ static void cleanup_session(gpointer user_data)
DBG("remove %s", session->session_path);
+ cleanup_nat_rules(session);
cleanup_routing_table(session);
cleanup_firewall_session(session);
@@ -444,6 +534,7 @@ static void cleanup_session(gpointer user_data)
update_session_state(session);
g_slist_free(session->user_allowed_bearers);
+ g_free(session->user_allowed_interface);
free_session(session);
}
@@ -455,6 +546,8 @@ struct creation_data {
/* user config */
enum connman_session_type type;
GSList *allowed_bearers;
+ char *allowed_interface;
+ bool source_ip_rule;
};
static void cleanup_creation_data(struct creation_data *creation_data)
@@ -466,6 +559,7 @@ static void cleanup_creation_data(struct creation_data *creation_data)
dbus_message_unref(creation_data->pending);
g_slist_free(creation_data->allowed_bearers);
+ g_free(creation_data->allowed_interface);
g_free(creation_data);
}
@@ -543,6 +637,7 @@ void connman_session_set_default_config(struct connman_session_config *config)
config->ecall = FALSE;
g_slist_free(config->allowed_bearers);
+ config->allowed_bearers = NULL;
add_default_bearer_types(&config->allowed_bearers);
}
@@ -628,18 +723,18 @@ static int parse_bearers(DBusMessageIter *iter, GSList **list)
return 0;
}
-static void filter_bearer(GSList *policy_bearers,
- enum connman_service_type bearer,
+static void filter_bearer(GSList *bearers,
+ enum connman_service_type policy,
GSList **list)
{
- enum connman_service_type policy;
+ enum connman_service_type bearer;
GSList *it;
- if (!policy_bearers)
+ if (!bearers)
return;
- for (it = policy_bearers; it; it = it->next) {
- policy = GPOINTER_TO_INT(it->data);
+ for (it = bearers; it; it = it->next) {
+ bearer = GPOINTER_TO_INT(it->data);
if (policy != bearer)
continue;
@@ -652,18 +747,29 @@ static void filter_bearer(GSList *policy_bearers,
static void apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers,
GSList **list)
{
- enum connman_service_type bearer;
+ enum connman_service_type policy_bearer;
GSList *it;
*list = NULL;
- for (it = bearers; it; it = it->next) {
- bearer = GPOINTER_TO_INT(it->data);
+ for (it = policy_bearers; it; it = it->next) {
+ policy_bearer = GPOINTER_TO_INT(it->data);
- filter_bearer(policy_bearers, bearer, list);
+ filter_bearer(bearers, policy_bearer, list);
}
}
+static char * apply_policy_on_interface(const char *policy_interface,
+ const char *user_interface)
+{
+ if (policy_interface)
+ return g_strdup(policy_interface);
+ else if (user_interface)
+ return g_strdup(user_interface);
+ else
+ return NULL;
+}
+
const char *connman_session_get_owner(struct connman_session *session)
{
return session->owner;
@@ -809,6 +915,28 @@ static void append_notify(DBusMessageIter *dict,
info_last->config.allowed_bearers = info->config.allowed_bearers;
}
+ if (session->append_all ||
+ info->config.allowed_interface != info_last->config.allowed_interface) {
+ char *ifname = info->config.allowed_interface;
+ if (!ifname)
+ ifname = "*";
+ connman_dbus_dict_append_basic(dict, "AllowedInterface",
+ DBUS_TYPE_STRING,
+ &ifname);
+ info_last->config.allowed_interface = info->config.allowed_interface;
+ }
+
+ if (session->append_all ||
+ info->config.source_ip_rule != info_last->config.source_ip_rule) {
+ dbus_bool_t source_ip_rule = FALSE;
+ if (info->config.source_ip_rule)
+ source_ip_rule = TRUE;
+ connman_dbus_dict_append_basic(dict, "SourceIPRule",
+ DBUS_TYPE_BOOLEAN,
+ &source_ip_rule);
+ info_last->config.source_ip_rule = info->config.source_ip_rule;
+ }
+
session->append_all = false;
}
@@ -828,7 +956,9 @@ static bool compute_notifiable_changes(struct connman_session *session)
return true;
if (info->config.allowed_bearers != info_last->config.allowed_bearers ||
- info->config.type != info_last->config.type)
+ info->config.type != info_last->config.type ||
+ info->config.allowed_interface != info_last->config.allowed_interface ||
+ info->config.source_ip_rule != info_last->config.source_ip_rule)
return true;
return false;
@@ -882,6 +1012,7 @@ int connman_session_config_update(struct connman_session *session)
{
struct session_info *info = session->info;
GSList *allowed_bearers;
+ char *allowed_interface;
int err;
DBG("session %p", session);
@@ -907,6 +1038,10 @@ int connman_session_config_update(struct connman_session *session)
session->user_allowed_bearers,
&allowed_bearers);
+ allowed_interface = apply_policy_on_interface(
+ session->policy_config->allowed_interface,
+ session->user_allowed_interface);
+
if (session->active)
set_active_session(session, false);
@@ -916,6 +1051,9 @@ int connman_session_config_update(struct connman_session *session)
g_slist_free(info->config.allowed_bearers);
info->config.allowed_bearers = allowed_bearers;
+ g_free(info->config.allowed_interface);
+ info->config.allowed_interface = allowed_interface;
+
session_activate(session);
info->config.type = apply_policy_on_type(
@@ -1024,6 +1162,7 @@ static DBusMessage *change_session(DBusConnection *conn,
session->active = false;
session_deactivate(session);
+ update_session_state(session);
g_slist_free(info->config.allowed_bearers);
session->user_allowed_bearers = allowed_bearers;
@@ -1044,6 +1183,38 @@ static DBusMessage *change_session(DBusConnection *conn,
info->config.type = apply_policy_on_type(
session->policy_config->type,
connman_session_parse_connection_type(val));
+ } else if (g_str_equal(name, "AllowedInterface")) {
+ dbus_message_iter_get_basic(&value, &val);
+ if (session->active)
+ set_active_session(session, false);
+
+ session->active = false;
+ session_deactivate(session);
+ update_session_state(session);
+
+ g_free(session->user_allowed_interface);
+ /* empty string means allow any interface */
+ if (!g_strcmp0(val, ""))
+ session->user_allowed_interface = NULL;
+ else
+ session->user_allowed_interface = g_strdup(val);
+
+ info->config.allowed_interface = apply_policy_on_interface(
+ session->policy_config->allowed_interface,
+ session->user_allowed_interface);
+
+ session_activate(session);
+ } else {
+ goto err;
+ }
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ if (g_str_equal(name, "SourceIPRule")) {
+ dbus_bool_t source_ip_rule;
+ dbus_message_iter_get_basic(&value, &source_ip_rule);
+
+ info->config.source_ip_rule = source_ip_rule;
+ update_session_state(session);
} else {
goto err;
}
@@ -1149,6 +1320,7 @@ static int session_policy_config_cb(struct connman_session *session,
goto err;
session->policy_config = config;
+ session->info->config.source_ip_rule = creation_data->source_ip_rule;
session->mark = session_mark++;
session->index = -1;
@@ -1177,11 +1349,18 @@ static int session_policy_config_cb(struct connman_session *session,
session->user_allowed_bearers = creation_data->allowed_bearers;
creation_data->allowed_bearers = NULL;
+ session->user_allowed_interface = creation_data->allowed_interface;
+ creation_data->allowed_interface = NULL;
+
apply_policy_on_bearers(
session->policy_config->allowed_bearers,
session->user_allowed_bearers,
&info->config.allowed_bearers);
+ info->config.allowed_interface = apply_policy_on_interface(
+ session->policy_config->allowed_interface,
+ session->user_allowed_interface);
+
g_hash_table_replace(session_hash, session->session_path, session);
DBG("add %s", session->session_path);
@@ -1206,6 +1385,8 @@ static int session_policy_config_cb(struct connman_session *session,
info_last->config.priority = info->config.priority;
info_last->config.roaming_policy = info->config.roaming_policy;
info_last->config.allowed_bearers = info->config.allowed_bearers;
+ info_last->config.allowed_interface = info->config.allowed_interface;
+ info_last->config.source_ip_rule = info->config.source_ip_rule;
session->append_all = true;
@@ -1293,11 +1474,29 @@ int __connman_session_create(DBusMessage *msg)
connman_session_parse_connection_type(val);
user_connection_type = true;
+ } else if (g_str_equal(key, "AllowedInterface")) {
+ dbus_message_iter_get_basic(&value, &val);
+ creation_data->allowed_interface = g_strdup(val);
} else {
err = -EINVAL;
goto err;
}
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ if (g_str_equal(key, "SourceIPRule")) {
+ dbus_bool_t source_ip_rule;
+ dbus_message_iter_get_basic(&value, &source_ip_rule);
+ creation_data->source_ip_rule = source_ip_rule;
+ } else {
+ err = -EINVAL;
+ goto err;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ goto err;
}
+
dbus_message_iter_next(&array);
}
@@ -1481,7 +1680,14 @@ static void update_session_state(struct connman_session *session)
DBG("session %p state %s", session, state2string(state));
+ update_firewall(session);
+ del_nat_rules(session);
update_routing_table(session);
+ add_nat_rules(session);
+
+ if (policy && policy->update_session_state)
+ policy->update_session_state(session, state);
+
session_notify(session);
}
@@ -1490,17 +1696,31 @@ static bool session_match_service(struct connman_session *session,
{
enum connman_service_type bearer_type;
enum connman_service_type service_type;
+ enum connman_service_type current_service_type;
GSList *list;
+ char *ifname;
if (policy && policy->allowed)
return policy->allowed(session, service);
+ current_service_type = connman_service_get_type(session->service);
+
for (list = session->info->config.allowed_bearers; list; list = list->next) {
bearer_type = GPOINTER_TO_INT(list->data);
service_type = connman_service_get_type(service);
+ ifname = connman_service_get_interface(service);
- if (bearer_type == service_type)
- return true;
+ if (bearer_type == current_service_type)
+ return false;
+
+ if (bearer_type == service_type &&
+ (session->info->config.allowed_interface == NULL ||
+ !g_strcmp0(session->info->config.allowed_interface, "*") ||
+ !g_strcmp0(session->info->config.allowed_interface, ifname))) {
+ g_free(ifname);
+ return true;
+ }
+ g_free(ifname);
}
return false;
@@ -1521,6 +1741,7 @@ static bool is_session_connected(struct connman_session *session,
case CONNMAN_SERVICE_STATE_READY:
if (session->info->config.type == CONNMAN_SESSION_TYPE_INTERNET)
return false;
+ /* fall through */
case CONNMAN_SERVICE_STATE_ONLINE:
return true;
}
@@ -1536,6 +1757,40 @@ static void session_activate(struct connman_session *session)
if (!service_hash)
return;
+ if (policy && policy->get_service_for_session) {
+ struct connman_service *service;
+ struct connman_service_info *info;
+ GSList *service_list = NULL;
+ enum connman_service_state state = CONNMAN_SESSION_STATE_DISCONNECTED;
+
+ g_hash_table_iter_init(&iter, service_hash);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct connman_service_info *info = value;
+ state = __connman_service_get_state(info->service);
+
+ if (is_session_connected(session, state))
+ service_list = g_slist_prepend(service_list,
+ info->service);
+ }
+
+ service_list = g_slist_reverse(service_list);
+ service = policy->get_service_for_session(session, service_list);
+
+ if (service) {
+ info = g_hash_table_lookup(service_hash, service);
+ DBG("session %p add service %p", session, info->service);
+
+ info->sessions = g_slist_prepend(info->sessions,
+ session);
+ session->service = info->service;
+ update_session_state(session);
+ }
+
+ g_slist_free(service_list);
+ return;
+ }
+
g_hash_table_iter_init(&iter, service_hash);
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct connman_service_info *info = value;
@@ -1630,10 +1885,10 @@ static void handle_service_state_offline(struct connman_service *service,
session->service = NULL;
update_session_state(session);
+ session_activate(session);
}
}
-
static void service_state_changed(struct connman_service *service,
enum connman_service_state state)
{
@@ -1693,7 +1948,7 @@ static void ipconfig_changed(struct connman_service *service,
continue;
if (session->service && session->service == service) {
- update_routing_table(session);
+ update_session_state(session);
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
ipconfig_ipv4_changed(session);
@@ -1730,12 +1985,6 @@ int __connman_session_init(void)
service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, cleanup_service);
- if (__connman_firewall_is_up()) {
- err = init_firewall();
- if (err < 0)
- return err;
- }
-
return 0;
}
@@ -1746,8 +1995,6 @@ void __connman_session_cleanup(void)
if (!connection)
return;
- cleanup_firewall();
-
connman_notifier_unregister(&session_notifier);
g_hash_table_foreach(session_hash, release_session, NULL);