[PATCH v2 24/42] vpn: Add support for dual-IP VPNs

Jussi Laakkonen <[email protected]> Wed, 13 Aug 2025 18:01:56 +0300
Newsgroups dev.linux.lists.connman
Message-ID <[email protected]>
Information sent by vpn-provider.c on dual-IP VPNs must be handled also
here properly. Adapt to using modified connman_provider_get_family().

Use with the modified connman_provider_get_family() to query which
families are supported to clean up the code.
---
 plugins/vpn.c | 126 +++++++++++++++++++++++++++++++++-----------------
 1 file changed, 84 insertions(+), 42 deletions(-)

diff --git a/plugins/vpn.c b/plugins/vpn.c
index c0739fc1..97ca72c5 100644
--- a/plugins/vpn.c
+++ b/plugins/vpn.c
@@ -1554,57 +1554,94 @@ static void set_route(struct connection_data *data, struct vpn_route *route)
 static int save_route(GHashTable *routes, int family, const char *network,
 			const char *netmask, const char *gateway);
 
-static int add_network_route(struct connection_data *data)
+
+static int set_network_route(struct connection_data *data, struct vpn_route *rt)
 {
-	struct vpn_route rt = { 0, };
 	int err;
 
-	if (!data)
-		return -EINVAL;
-
-	rt.family = connman_provider_get_family(data->provider);
-	switch (rt.family) {
-	case PF_INET:
-		err = connman_inet_get_route_addresses(data->index,
-					&rt.network, &rt.netmask, &rt.gateway);
-		break;
-	case PF_INET6:
-		err = connman_inet_ipv6_get_route_addresses(data->index,
-					&rt.network, &rt.netmask, &rt.gateway);
-		break;
-	default:
-		connman_error("invalid protocol family %d", rt.family);
+	if (!data || !rt)
 		return -EINVAL;
-	}
 
 	DBG("network %s gateway %s netmask %s for provider %p",
-						rt.network, rt.gateway, rt.netmask,
-						data->provider);
+					rt->network, rt->gateway, rt->netmask,
+					data->provider);
 
-	if (err) {
-		connman_error("cannot get network/gateway/netmask for %p",
-							data->provider);
-		goto out;
-	}
-
-	err = save_route(data->server_routes, rt.family, rt.network, rt.netmask,
-				rt.gateway);
+	err = save_route(data->server_routes, rt->family, rt->network,
+						rt->netmask, rt->gateway);
 	if (err) {
 		connman_warn("failed to add network route for provider"
 					"%p", data->provider);
-		goto out;
+		return err;
 	}
 
-	set_route(data, &rt);
-
-out:
-	g_free(rt.network);
-	g_free(rt.netmask);
-	g_free(rt.gateway);
+	set_route(data, rt);
 
 	return 0;
 }
 
+static void free_network_route(struct vpn_route *rt)
+{
+	g_free(rt->network);
+	rt->network = NULL;
+
+	g_free(rt->netmask);
+	rt->network = NULL;
+
+	g_free(rt->gateway);
+	rt->network = NULL;
+}
+
+static int add_network_route(struct connection_data *data)
+{
+	struct vpn_route rt = { 0, };
+	int err = 0;
+
+	if (!data)
+		return -EINVAL;
+
+	if (connman_provider_get_family(data->provider, AF_INET)) {
+		rt.family = AF_INET;
+		err = connman_inet_get_route_addresses(data->index,
+					&rt.network, &rt.netmask,
+					&rt.gateway);
+		if (err) {
+			connman_error("cannot get IPv4 network/gateway/netmask "
+						"for %p/%s", data->provider,
+						data->ident);
+		} else {
+			err = set_network_route(data, &rt);
+			if (err)
+				connman_error("cannot set IPv4 network route "
+						"for %p/%s", data->provider,
+						data->ident);
+		}
+
+		free_network_route(&rt);
+	}
+
+	if (connman_provider_get_family(data->provider, AF_INET6)) {
+		rt.family = AF_INET6;
+		err = connman_inet_ipv6_get_route_addresses(data->index,
+					&rt.network, &rt.netmask,
+					&rt.gateway);
+		if (err) {
+			connman_error("cannot get IPv6 network/gateway/netmask "
+						"for %p/%s", data->provider,
+						data->ident);
+		} else {
+			err = set_network_route(data, &rt);
+			if (err)
+				connman_error("cannot set IPv6 network route "
+						"for %p/%s", data->provider,
+						data->ident);
+		}
+
+		free_network_route(&rt);
+	}
+
+	return err;
+}
+
 static bool is_valid_route_table(struct connman_provider *provider,
 							GHashTable *table)
 {
@@ -1667,6 +1704,14 @@ static bool check_routes(struct connman_provider *provider)
 	return false;
 }
 
+static void set_default_route(struct connection_data *data, int family,
+							char *ipaddr_any)
+{
+	struct vpn_route def_route = {family, ipaddr_any, ipaddr_any, NULL};
+
+	set_route(data, &def_route);
+}
+
 static int set_routes(struct connman_provider *provider,
 				enum connman_provider_route_type type)
 {
@@ -1699,13 +1744,10 @@ static int set_routes(struct connman_provider *provider,
 	/* If non-split routed VPN does not have a default route, add it */
 	if (!connman_provider_is_split_routing(provider) &&
 						!data->default_route_set) {
-		int family = connman_provider_get_family(provider);
-		const char *ipaddr_any = family == AF_INET6 ?
-							"::" : "0.0.0.0";
-		struct vpn_route def_route = {family, (char*) ipaddr_any,
-						(char*) ipaddr_any, NULL};
-
-		set_route(data, &def_route);
+		if (connman_provider_get_family(provider, AF_INET))
+			set_default_route(data, AF_INET, "0.0.0.0");
+		if (connman_provider_get_family(provider, AF_INET6))
+			set_default_route(data, AF_INET6, "::");
 	}
 
 	/* Split routed VPN must have at least one route to the network */
-- 
2.39.5