]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
GP: Fix the issue of a 0.0.0.0/0 "split"-include route by swapping the "split" route...
authorDaniel Lenski <dlenski@gmail.com>
Sun, 24 May 2020 18:47:37 +0000 (11:47 -0700)
committerDaniel Lenski <dlenski@gmail.com>
Wed, 4 Nov 2020 22:29:18 +0000 (22:29 +0000)
GlobalProtect VPNs always or almost always send `<netmask>255.255.255.255</netmask>` (host route). If they
wish to include a true IPv4 default route (`0.0.0.0/0`), they send it a "split"-include route.

This interferes with NetworkManager users’ ability to use the "Use only for
resources on this connection" feature of NM's VPN plugins.  (Which basically
tells NM to use only split routes from the connection, and ignore a default route.)

This patch detects the case of a 0.0.0.0/0 IPv4 "split"-include route, and swaps it to become the default
default route.

See: https://gitlab.gnome.org/GNOME/NetworkManager-openconnect/-/merge_requests/12#note_818780

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
gpst.c

diff --git a/gpst.c b/gpst.c
index 686bde2baf8fe23f0e9aa5ec6c609b978de852b9..80d669897a8fe6896b3452a6b142c4f5e4620d59 100644 (file)
--- a/gpst.c
+++ b/gpst.c
@@ -456,7 +456,9 @@ out:
 static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_node, void *cb_data)
 {
        xmlNode *member;
-       char *s = NULL;
+       char *s = NULL, *deferred_netmask = NULL;
+       struct oc_split_include *inc;
+       int split_route_is_default_route = 0;
        int ii;
 
        if (!xml_node || !xmlnode_is_named(xml_node, "response"))
@@ -480,9 +482,12 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_
        for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) {
                if (!xmlnode_get_val(xml_node, "ip-address", &s))
                        vpninfo->ip_info.addr = add_option(vpninfo, "ipaddr", &s);
-               else if (!xmlnode_get_val(xml_node, "netmask", &s))
-                       vpninfo->ip_info.netmask = add_option(vpninfo, "netmask", &s);
-               else if (!xmlnode_get_val(xml_node, "mtu", &s))
+               else if (!xmlnode_get_val(xml_node, "netmask", &deferred_netmask)) {
+                       /* XX: GlobalProtect servers always (almost always?) send 255.255.255.255 as their netmask
+                        * (a /32 host route), and if they want to include an actual default route (0.0.0.0/0)
+                        * they instead put it under <access-routes/>. We defer saving the netmask until later.
+                        */
+               } else if (!xmlnode_get_val(xml_node, "mtu", &s))
                        vpninfo->ip_info.mtu = atoi(s);
                else if (!xmlnode_get_val(xml_node, "lifetime", &s))
                        vpn_progress(vpninfo, PRG_INFO, _("Session will expire after %d minutes.\n"), atoi(s)/60);
@@ -532,10 +537,19 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_
                } else if (xmlnode_is_named(xml_node, "access-routes") || xmlnode_is_named(xml_node, "exclude-access-routes")) {
                        for (member = xml_node->children; member; member=member->next) {
                                if (!xmlnode_get_val(member, "member", &s)) {
-                                       struct oc_split_include *inc = malloc(sizeof(*inc));
-                                       if (!inc)
+                                       int is_inc = xmlnode_is_named(xml_node, "access-routes");
+
+                                       /* XX: if this is a default route jammed into the split-include
+                                        * routes, just mark it for now.
+                                        */
+                                       if (is_inc && !strcmp(s, "0.0.0.0/0")) {
+                                               split_route_is_default_route = 1;
                                                continue;
-                                       if (xmlnode_is_named(xml_node, "access-routes")) {
+                                       }
+
+                                       if ((inc = malloc(sizeof(*inc))) == NULL)
+                                               return -ENOMEM;
+                                       if (is_inc) {
                                                inc->route = add_option(vpninfo, "split-include", &s);
                                                inc->next = vpninfo->ip_info.split_includes;
                                                vpninfo->ip_info.split_includes = inc;
@@ -599,6 +613,34 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_
                }
        }
 
+       /* Fix the issue of a 0.0.0.0/0 "split"-include route by swapping the "split" route with the default netmask. */
+       if (split_route_is_default_route) {
+               char *original_netmask = deferred_netmask;
+
+               if ((deferred_netmask = strdup("0.0.0.0")) == NULL)
+                       return -ENOMEM;
+
+               /* If the original netmask wasn't /32, add it as a split route */
+               if (vpninfo->ip_info.addr && original_netmask) {
+                       uint32_t nm_bits = inet_addr(original_netmask);
+                       if (nm_bits != 0xffffffff) { /* 255.255.255.255 */
+                               struct in_addr net_addr;
+                               inet_aton(vpninfo->ip_info.addr, &net_addr);
+                               net_addr.s_addr &= nm_bits; /* clear host bits */
+
+                               if ((inc = malloc(sizeof(*inc))) == NULL ||
+                                   asprintf(&s, "%s/%s", inet_ntoa(net_addr), original_netmask) <= 0)
+                                       return -ENOMEM;
+                               inc->route = add_option(vpninfo, "split-include", &s);
+                               inc->next = vpninfo->ip_info.split_includes;
+                               vpninfo->ip_info.split_includes = inc;
+                       }
+               }
+               free(original_netmask);
+       }
+       if (deferred_netmask)
+               vpninfo->ip_info.netmask = add_option(vpninfo, "netmask", &deferred_netmask);
+
        /* Set 10-second DPD/keepalive (same as Windows client) unless
         * overridden with --force-dpd */
        if (!vpninfo->ssl_times.dpd)