#include <gdbus.h>
-#include <connman/session.h>
-
#include "connman.h"
static DBusConnection *connection;
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;
struct connman_service *service_last;
struct connman_session_config *policy_config;
GSList *user_allowed_bearers;
+ char *user_allowed_interface;
bool ecall;
struct firewall_context *fw;
uint32_t mark;
int index;
+ char *addr;
char *gateway;
+ unsigned char prefixlen;
bool policy_routing;
+ bool snat_enabled;
};
struct connman_service_info {
GSList *sessions;
};
+struct fw_snat {
+ GSList *sessions;
+ int id;
+ int index;
+ char *addr;
+ 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);
case CONNMAN_SERVICE_TYPE_GPS:
case CONNMAN_SERVICE_TYPE_P2P:
case CONNMAN_SERVICE_TYPE_UNKNOWN:
+#if defined TIZEN_EXT_WIFI_MESH
+ case CONNMAN_SERVICE_TYPE_MESH:
+#endif
return "";
}
return "";
}
-static int init_firewall(void)
+static struct fw_snat *fw_snat_lookup(int index, const char *addr)
{
- 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) {
+ if (g_strcmp0(addr, fw_snat->addr) != 0)
+ continue;
+ 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->addr = g_strdup(addr);
+
+ 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->addr);
+ 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)
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;
{
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("");
if (!session->gateway)
return;
- DBG("index %d routing table %d default gateway %s",
- session->index, session->mark, session->gateway);
+ DBG("index %d routing table %d default gateway %s/%u",
+ session->index, session->mark, session->gateway, session->prefixlen);
+
+ __connman_inet_del_subnet_from_table(session->mark,
+ session->index, session->gateway, session->prefixlen);
__connman_inet_del_default_from_table(session->mark,
session->index, session->gateway);
g_free(session->gateway);
session->gateway = NULL;
+ session->prefixlen = 0;
session->index = -1;
}
{
struct connman_ipconfig *ipconfig;
int err;
+ struct in_addr addr = { INADDR_ANY };
if (!session->service)
return;
session->index = __connman_ipconfig_get_index(ipconfig);
session->gateway = g_strdup(__connman_ipconfig_get_gateway(ipconfig));
- DBG("index %d routing table %d default gateway %s",
- session->index, session->mark, session->gateway);
+ if (!session->gateway)
+ session->gateway = g_strdup(inet_ntoa(addr));
+
+ session->prefixlen = __connman_ipconfig_get_prefixlen(ipconfig);
+
+ DBG("index %d routing table %d default gateway %s/%u",
+ session->index, session->mark, session->gateway, session->prefixlen);
err = __connman_inet_add_default_to_table(session->mark,
session->index, session->gateway);
if (err < 0)
DBG("session %p %s", session, strerror(-err));
+
+ err = __connman_inet_add_subnet_to_table(session->mark,
+ session->index, session->gateway, session->prefixlen);
+ if (err < 0)
+ DBG("session add subnet route %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, session->addr);
+
+ 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;
+
+ g_free(session->addr);
+ session->addr = g_strdup(addr);
+
+ session->snat_enabled = true;
+ fw_snat = fw_snat_lookup(index, session->addr);
+ 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)
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) {
if (session->notify_watch > 0)
g_dbus_remove_watch(connection, session->notify_watch);
+ g_dbus_unregister_interface(connection, session->session_path,
+ CONNMAN_SESSION_INTERFACE);
+
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);
g_free(session->info);
g_free(session->info_last);
g_free(session->gateway);
+ g_free(session->addr);
g_free(session);
}
DBG("remove %s", session->session_path);
+ cleanup_nat_rules(session);
cleanup_routing_table(session);
cleanup_firewall_session(session);
update_session_state(session);
g_slist_free(session->user_allowed_bearers);
+ g_free(session->user_allowed_interface);
free_session(session);
}
/* user config */
enum connman_session_type type;
GSList *allowed_bearers;
+ char *allowed_interface;
+ bool source_ip_rule;
+ char *context_identifier;
};
static void cleanup_creation_data(struct creation_data *creation_data)
if (creation_data->pending)
dbus_message_unref(creation_data->pending);
+ if (creation_data->context_identifier)
+ g_free(creation_data->context_identifier);
g_slist_free(creation_data->allowed_bearers);
+ g_free(creation_data->allowed_interface);
g_free(creation_data);
}
config->ecall = FALSE;
g_slist_free(config->allowed_bearers);
+ config->allowed_bearers = NULL;
add_default_bearer_types(&config->allowed_bearers);
}
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;
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;
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.context_identifier != info_last->config.context_identifier) {
+ char *ifname = info->config.context_identifier;
+ if (!ifname)
+ ifname = "";
+ connman_dbus_dict_append_basic(dict, "ContextIdentifier",
+ DBUS_TYPE_STRING,
+ &ifname);
+ info_last->config.context_identifier = info->config.context_identifier;
+ }
+
+ 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;
}
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;
{
struct session_info *info = session->info;
GSList *allowed_bearers;
+ char *allowed_interface;
int err;
DBG("session %p", 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);
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(
session->active = false;
session_deactivate(session);
+ update_session_state(session);
g_slist_free(info->config.allowed_bearers);
session->user_allowed_bearers = allowed_bearers;
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;
}
goto err;
session->policy_config = config;
+ session->info->config.source_ip_rule = creation_data->source_ip_rule;
session->mark = session_mark++;
session->index = -1;
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);
connman_error("Failed to register %s", session->session_path);
g_hash_table_remove(session_hash, session->session_path);
err = -EINVAL;
- goto err;
+ goto err_notify;
}
reply = g_dbus_create_reply(creation_data->pending,
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;
return 0;
err:
+ cleanup_session(session);
+
+err_notify:
reply = __connman_error_failed(creation_data->pending, -err);
g_dbus_send_message(connection, reply);
creation_data->pending = NULL;
- cleanup_session(session);
cleanup_creation_data(creation_data);
return err;
connman_session_parse_connection_type(val);
user_connection_type = true;
+ } else if (g_str_equal(key, "ContextIdentifier")) {
+ dbus_message_iter_get_basic(&value, &val);
+ creation_data->context_identifier = g_strdup(val);
+ } 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);
}
enum connman_session_state state = CONNMAN_SESSION_STATE_DISCONNECTED;
if (session->service) {
- service_state = __connman_service_get_state(session->service);
+ service_state = connman_service_get_state(session->service);
state = service_to_session_state(service_state);
session->info->state = state;
}
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);
}
{
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 defined TIZEN_EXT
+ if (bearer_type == current_service_type) {
+ g_free(ifname);
+ return false;
+ }
+#else
+ if (bearer_type == current_service_type)
+ return false;
+#endif
- if (bearer_type == service_type)
- return true;
+ 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;
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;
}
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;
enum connman_service_state state;
- state = __connman_service_get_state(info->service);
+ state = connman_service_get_state(info->service);
if (is_session_connected(session, state) &&
session_match_service(session, info->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)
{
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);
}
}
-static struct connman_notifier session_notifier = {
+static const struct connman_notifier session_notifier = {
.name = "session",
.service_state_changed = service_state_changed,
.ipconfig_changed = ipconfig_changed,
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;
}
if (!connection)
return;
- cleanup_firewall();
-
connman_notifier_unregister(&session_notifier);
g_hash_table_foreach(session_hash, release_session, NULL);