+out:
+ g_free(params->vpn_gateway);
+ g_free(params);
+}
+
+static void set_vpn_routes(struct gateway_data *new_gateway,
+ struct connman_service *service,
+ const char *gateway,
+ enum connman_ipconfig_type type,
+ const char *peer,
+ struct gateway_data *active_gateway)
+{
+ struct gateway_config *config;
+ struct connman_ipconfig *ipconfig;
+ char *dest;
+
+ DBG("new %p service %p gw %s type %d peer %s active %p",
+ new_gateway, service, gateway, type, peer, active_gateway);
+
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+ ipconfig = __connman_service_get_ip4config(service);
+ config = new_gateway->ipv4_gateway;
+ } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+ ipconfig = __connman_service_get_ip6config(service);
+ config = new_gateway->ipv6_gateway;
+ } else
+ return;
+
+ if (config) {
+ int index = __connman_ipconfig_get_index(ipconfig);
+ struct get_gateway_params *params;
+
+ config->vpn = true;
+ if (peer)
+ config->vpn_ip = g_strdup(peer);
+ else if (gateway)
+ config->vpn_ip = g_strdup(gateway);
+
+ params = g_try_malloc(sizeof(struct get_gateway_params));
+ if (!params)
+ return;
+
+ params->vpn_index = index;
+ params->vpn_gateway = g_strdup(gateway);
+
+ /*
+ * Find the gateway that is serving the VPN link
+ */
+ __connman_inet_get_route(gateway, get_gateway_cb, params);
+ }
+
+ if (!active_gateway)
+ return;
+
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+ /*
+ * 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.
+ */
+ if (!active_gateway->ipv4_gateway)
+ return;
+
+
+ /*
+ * If VPN server is on same subnet as we are, skip adding
+ * route.
+ */
+ if (connman_inet_compare_subnet(active_gateway->index,
+ gateway))
+ return;
+
+ DBG("active gw %s", active_gateway->ipv4_gateway->gateway);
+
+ if (g_strcmp0(active_gateway->ipv4_gateway->gateway,
+ "0.0.0.0") != 0)
+ dest = active_gateway->ipv4_gateway->gateway;
+ else
+ dest = NULL;
+
+ connman_inet_add_host_route(active_gateway->index, gateway,
+ dest);
+
+ } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+
+ if (!active_gateway->ipv6_gateway)
+ return;
+
+ if (connman_inet_compare_ipv6_subnet(active_gateway->index,
+ gateway))
+ return;
+
+ DBG("active gw %s", active_gateway->ipv6_gateway->gateway);
+
+ if (g_strcmp0(active_gateway->ipv6_gateway->gateway,
+ "::") != 0)
+ dest = active_gateway->ipv6_gateway->gateway;
+ else
+ dest = NULL;
+
+ connman_inet_add_ipv6_host_route(active_gateway->index,
+ gateway, dest);
+ }
+}
+
+static int del_routes(struct gateway_data *data,
+ enum connman_ipconfig_type type)
+{
+ int status4 = 0, status6 = 0;
+ bool do_ipv4 = false, do_ipv6 = false;
+
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+ do_ipv4 = true;
+ else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+ do_ipv6 = true;
+ else
+ do_ipv4 = do_ipv6 = true;
+
+ if (do_ipv4 && data->ipv4_gateway) {
+ if (data->ipv4_gateway->vpn) {
+ status4 = connman_inet_clear_gateway_address(
+ data->index,
+ data->ipv4_gateway->vpn_ip);
+
+ } else if (g_strcmp0(data->ipv4_gateway->gateway,
+ "0.0.0.0") == 0) {
+ status4 = connman_inet_clear_gateway_interface(
+ data->index);
+ } else {
+ connman_inet_del_host_route(data->index,
+ data->ipv4_gateway->gateway);
+ status4 = connman_inet_clear_gateway_address(
+ data->index,
+ data->ipv4_gateway->gateway);
+ }
+ }
+
+ if (do_ipv6 && data->ipv6_gateway) {
+ if (data->ipv6_gateway->vpn) {
+ status6 = connman_inet_clear_ipv6_gateway_address(
+ data->index,
+ data->ipv6_gateway->vpn_ip);
+
+ } else if (g_strcmp0(data->ipv6_gateway->gateway, "::") == 0) {
+ status6 = connman_inet_clear_ipv6_gateway_interface(
+ data->index);
+ } else {
+ connman_inet_del_ipv6_host_route(data->index,
+ data->ipv6_gateway->gateway);
+ status6 = connman_inet_clear_ipv6_gateway_address(
+ data->index,
+ data->ipv6_gateway->gateway);
+ }
+ }
+
+ return (status4 < 0 ? status4 : status6);
+}
+
+static int disable_gateway(struct gateway_data *data,
+ enum connman_ipconfig_type type)
+{
+ bool active = false;
+
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+ if (data->ipv4_gateway)
+ active = data->ipv4_gateway->active;
+ } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+ if (data->ipv6_gateway)
+ active = data->ipv6_gateway->active;
+ } else
+ active = true;
+
+ DBG("type %d active %d", type, active);
+
+ if (active)
+ return del_routes(data, type);
+
+ return 0;
+}
+
+static struct gateway_data *add_gateway(struct connman_service *service,
+ int index, const char *gateway,
+ enum connman_ipconfig_type type)
+{
+ struct gateway_data *data, *old;
+ struct gateway_config *config;
+
+ if (!gateway || strlen(gateway) == 0)