connection: Set host route to VPN server
[framework/connectivity/connman.git] / src / connection.c
index 11e42c9..f4abd57 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -360,6 +360,8 @@ static void set_default_gateway(struct gateway_data *data,
                                        data->ipv4_gateway->vpn == TRUE) {
                connman_inet_set_gateway_address(data->index,
                                                data->ipv4_gateway->vpn_ip);
+               connman_inet_add_host_route(data->index,
+                                       data->ipv4_gateway->vpn_ip, NULL);
                data->ipv4_gateway->active = TRUE;
 
                DBG("set %p index %d vpn %s index %d phy %s",
@@ -376,6 +378,8 @@ static void set_default_gateway(struct gateway_data *data,
                                        data->ipv6_gateway->vpn == TRUE) {
                connman_inet_set_ipv6_gateway_address(data->index,
                                                data->ipv6_gateway->vpn_ip);
+               connman_inet_add_ipv6_host_route(data->index,
+                                       data->ipv6_gateway->vpn_ip, NULL);
                data->ipv6_gateway->active = TRUE;
 
                DBG("set %p index %d vpn %s index %d phy %s",
@@ -725,6 +729,54 @@ void __connman_connection_gateway_activate(struct connman_service *service,
                data->ipv6_gateway->active = TRUE;
 }
 
+static void add_host_route(int family, int index, const char *gateway,
+                       enum connman_service_type service_type)
+{
+       switch (family) {
+       case AF_INET:
+               if (g_strcmp0(gateway, "0.0.0.0") != 0) {
+                       /*
+                        * We must not set route to the phy dev gateway in
+                        * VPN link. The packets to VPN link might be routed
+                        * back to itself and not routed into phy link gateway.
+                        */
+                       if (service_type != CONNMAN_SERVICE_TYPE_VPN)
+                               connman_inet_add_host_route(index, gateway,
+                                                                       NULL);
+               } else {
+                       /*
+                        * Add host route to P-t-P link so that services can
+                        * be moved around and we can have some link to P-t-P
+                        * network (although those P-t-P links have limited
+                        * usage if default route is not directed to them)
+                        */
+                       char *dest;
+                       if (connman_inet_get_dest_addr(index, &dest) == 0) {
+                               connman_inet_add_host_route(index, dest, NULL);
+                               g_free(dest);
+                       }
+               }
+               break;
+
+       case AF_INET6:
+               if (g_strcmp0(gateway, "::") != 0) {
+                       if (service_type != CONNMAN_SERVICE_TYPE_VPN)
+                               connman_inet_add_ipv6_host_route(index,
+                                                               gateway, NULL);
+               } else {
+                       /* P-t-P link, add route to destination */
+                       char *dest;
+                       if (connman_inet_ipv6_get_dest_addr(index,
+                                                               &dest) == 0) {
+                               connman_inet_add_ipv6_host_route(index, dest,
+                                                               NULL);
+                               g_free(dest);
+                       }
+               }
+               break;
+       }
+}
+
 int __connman_connection_gateway_add(struct connman_service *service,
                                        const char *gateway,
                                        enum connman_ipconfig_type type,
@@ -734,6 +786,8 @@ int __connman_connection_gateway_add(struct connman_service *service,
        struct gateway_data *new_gateway = NULL;
        enum connman_ipconfig_type type4 = CONNMAN_IPCONFIG_TYPE_UNKNOWN,
                type6 = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+       enum connman_service_type service_type =
+                                       connman_service_get_type(service);
        int index;
 
        index = __connman_service_get_index(service);
@@ -761,24 +815,9 @@ int __connman_connection_gateway_add(struct connman_service *service,
        DBG("active %p index %d new %p", active_gateway,
                active_gateway ? active_gateway->index : -1, new_gateway);
 
-       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
-                       new_gateway->ipv6_gateway != NULL &&
-                       g_strcmp0(new_gateway->ipv6_gateway->gateway,
-                                                               "::") != 0)
-               connman_inet_add_ipv6_host_route(index,
-                                       new_gateway->ipv6_gateway->gateway,
-                                       NULL);
-
-       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
-                       new_gateway->ipv4_gateway != NULL &&
-                       g_strcmp0(new_gateway->ipv4_gateway->gateway,
-                                                       "0.0.0.0") != 0)
-               connman_inet_add_host_route(index,
-                                       new_gateway->ipv4_gateway->gateway,
-                                       NULL);
-
        if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
                                new_gateway->ipv4_gateway != NULL) {
+               add_host_route(AF_INET, index, gateway, service_type);
                __connman_service_nameserver_add_routes(service,
                                        new_gateway->ipv4_gateway->gateway);
                type4 = CONNMAN_IPCONFIG_TYPE_IPV4;
@@ -786,21 +825,38 @@ int __connman_connection_gateway_add(struct connman_service *service,
 
        if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
                                new_gateway->ipv6_gateway != NULL) {
+               add_host_route(AF_INET6, index, gateway, service_type);
                __connman_service_nameserver_add_routes(service,
                                        new_gateway->ipv6_gateway->gateway);
                type6 = CONNMAN_IPCONFIG_TYPE_IPV6;
        }
 
-       if (connman_service_get_type(service) == CONNMAN_SERVICE_TYPE_VPN) {
-               if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
-                                       new_gateway->ipv4_gateway != NULL)
-                       set_vpn_routes(new_gateway->ipv4_gateway,
-                               service, gateway, type, peer);
+       if (service_type == CONNMAN_SERVICE_TYPE_VPN) {
 
-               else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
-                                       new_gateway->ipv6_gateway != NULL)
-                       set_vpn_routes(new_gateway->ipv6_gateway,
-                               service, gateway, type, peer);
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+                       if (new_gateway->ipv4_gateway != NULL)
+                               set_vpn_routes(new_gateway->ipv4_gateway,
+                                       service, gateway, type, peer);
+
+                       /*
+                        * Special route to VPN server via gateway. This
+                        * is needed so that we can access hosts behind
+                        * the VPN. The route might already exist depending
+                        * on network topology.
+                        */
+                       connman_inet_add_host_route(active_gateway->index,
+                                       gateway,
+                                       active_gateway->ipv4_gateway->gateway);
+
+               } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+                       if (new_gateway->ipv6_gateway != NULL)
+                               set_vpn_routes(new_gateway->ipv6_gateway,
+                                       service, gateway, type, peer);
+
+                       connman_inet_add_ipv6_host_route(active_gateway->index,
+                                       gateway,
+                                       active_gateway->ipv6_gateway->gateway);
+               }
 
        } else {
                if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
@@ -985,6 +1041,28 @@ gboolean __connman_connection_update_gateway(void)
        return updated;
 }
 
+int __connman_connection_get_vpn_index(int phy_index)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+       }
+
+       return -1;
+}
+
 int __connman_connection_init(void)
 {
        int err;