]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
use IPv6 interface identifier to build link-local address if we don't have a global one
authorDaniel Lenski <dlenski@gmail.com>
Mon, 18 May 2020 01:28:19 +0000 (18:28 -0700)
committerDaniel Lenski <dlenski@gmail.com>
Mon, 18 May 2020 04:10:43 +0000 (21:10 -0700)
Per RFC5072 (https://tools.ietf.org/html/rfc5072), that's what we're supposed to do with it.

See https://gitlab.com/openconnect/openconnect/-/commit/9f387fb21a0243da667521c2f2bca780bab3757c#note_343872233

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
f5.c
ppp.c
ppp.h

diff --git a/f5.c b/f5.c
index 44de5b3b1e19a299819f2e9dbf9197e2f69491d9..55a7e655426466eb75a5ba989195a13862ae1848 100644 (file)
--- a/f5.c
+++ b/f5.c
@@ -285,7 +285,7 @@ static int parse_options(struct openconnect_info *vpninfo, char *buf, int len,
        if (default_route && *ipv4)
                vpninfo->ip_info.netmask = strdup("0.0.0.0");
        if (default_route && *ipv6)
-               vpninfo->ip_info.netmask6 = strdup("::");
+               vpninfo->ip_info.netmask6 = strdup("::/0");
        if (buf_error(domains) == 0 && domains->pos > 0) {
                domains->data[domains->pos-1] = '\0';
                vpninfo->ip_info.domain = add_option(vpninfo, "search", &domains->data);
@@ -315,6 +315,7 @@ static int get_ip_address(struct openconnect_info *vpninfo, char *header, char *
        } else if (!strcasecmp(header, "X-VPN-client-IPv6")) {
                vpn_progress(vpninfo, PRG_INFO,
                             _("Got IPv6 address %s\n"), val);
+               /* XX: Should we treat this as a /64 netmask? Or an /128 address? */
                vpninfo->ip_info.addr6 = s = strdup(val);
                if (!s) return -ENOMEM;
        }
diff --git a/ppp.c b/ppp.c
index c1846aa2ad08a3c828173e84559ad332f5e2038a..8b9d6e19c8e7e4d59ccc463e6cf930df27bc6788 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -221,14 +221,17 @@ int openconnect_ppp_new(struct openconnect_info *vpninfo,
        if (!vpninfo->ip_info.dns[0] && !vpninfo->ip_info.nbns[0])
                ppp->solicit_peerns |= IPCP_DNS0|IPCP_DNS1|IPCP_NBNS0|IPCP_NBNS1;
 
-       /* Outgoing IPv4 address and IPv6 interface identifier (64 LSBs),
+       /* Outgoing IPv4 address and IPv6 interface identifier bits,
         * if already configured via another mechanism */
        if (vpninfo->ip_info.addr)
-               ppp->out_peer_addr.s_addr = inet_addr(vpninfo->ip_info.addr);
-       if (vpninfo->ip_info.addr6) {
-               unsigned char *bytes[16];
-               inet_pton(AF_INET6, vpninfo->ip_info.addr6, bytes);
-               memcpy(&ppp->out_ipv6_int_ident, bytes+8, 8);
+               ppp->out_ipv4_addr.s_addr = inet_addr(vpninfo->ip_info.addr);
+       if (vpninfo->ip_info.netmask6) {
+               char *slash = strchr(vpninfo->ip_info.netmask6, '/');
+               if (slash) *slash=0;
+               inet_pton(AF_INET6, vpninfo->ip_info.netmask6, &ppp->out_ipv6_addr);
+               if (slash) *slash='/';
+       } else if (vpninfo->ip_info.addr6) {
+               inet_pton(AF_INET6, vpninfo->ip_info.addr6, &ppp->out_ipv6_addr);
        }
 
        ppp->encap = encap;
@@ -266,12 +269,16 @@ int openconnect_ppp_new(struct openconnect_info *vpninfo,
 static void print_ppp_state(struct openconnect_info *vpninfo, int level)
 {
        struct oc_ppp *ppp = vpninfo->ppp;
+       char buf[40] = {0};
 
        vpn_progress(vpninfo, level, _("Current PPP state: %s (encap %s):\n"), ppps_names[ppp->ppp_state], encap_names[ppp->encap]);
-       vpn_progress(vpninfo, level, _("    in: asyncmap=0x%08x, lcp_opts=%d, lcp_magic=0x%08x, peer=%s\n"),
-                    ppp->in_asyncmap, ppp->in_lcp_opts, ntohl(ppp->in_lcp_magic), inet_ntoa(ppp->in_peer_addr));
-       vpn_progress(vpninfo, level, _("   out: asyncmap=0x%08x, lcp_opts=%d, lcp_magic=0x%08x, peer=%s, solicit_peerns=%d\n"),
-                    ppp->out_asyncmap, ppp->out_lcp_opts, ntohl(ppp->out_lcp_magic), inet_ntoa(ppp->out_peer_addr), ppp->solicit_peerns);
+       vpn_progress(vpninfo, level, _("    in: asyncmap=0x%08x, lcp_opts=%d, lcp_magic=0x%08x, ipv4=%s, ipv6=%s\n"),
+                    ppp->in_asyncmap, ppp->in_lcp_opts, ntohl(ppp->in_lcp_magic), inet_ntoa(ppp->in_ipv4_addr),
+                    inet_ntop(AF_INET6, &ppp->in_ipv6_addr, buf, sizeof(buf)));
+       inet_ntop(AF_INET6, &ppp->out_ipv6_addr, buf, sizeof(buf));
+       vpn_progress(vpninfo, level, _("   out: asyncmap=0x%08x, lcp_opts=%d, lcp_magic=0x%08x, ipv4=%s, ipv6=%s, solicit_peerns=%d\n"),
+                    ppp->out_asyncmap, ppp->out_lcp_opts, ntohl(ppp->out_lcp_magic), inet_ntoa(ppp->out_ipv4_addr),
+                    inet_ntop(AF_INET6, &ppp->out_ipv6_addr, buf, sizeof(buf)), ppp->solicit_peerns);
 }
 
 static int buf_append_ppp_tlv(struct oc_text_buf *buf, int tag, int len, const void *data)
@@ -402,17 +409,23 @@ static int handle_config_request(struct openconnect_info *vpninfo,
                        }
                        goto unknown;
                case PROTO_TAG_LEN(PPP_IPCP, IPCP_IPADDR, 4):
-                       memcpy(&ppp->in_peer_addr, p+2, 4);
+                       memcpy(&ppp->in_ipv4_addr, p+2, 4);
                        vpn_progress(vpninfo, PRG_DEBUG,
                                     _("Received peer IPv4 address %s from server\n"),
-                                    inet_ntoa(ppp->in_peer_addr));
+                                    inet_ntoa(ppp->in_ipv4_addr));
                        break;
-               case PROTO_TAG_LEN(PPP_IP6CP, IP6CP_INT_ID, 8):
-                       memcpy(&ppp->in_ipv6_int_ident, p+2, 8);
+               case PROTO_TAG_LEN(PPP_IP6CP, IP6CP_INT_ID, 8): {
+                       char buf[40];
+                       unsigned char ipv6_ll[16] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0};
+                       memcpy(ipv6_ll + 8, p+2, 8);
+                       memcpy(&ppp->in_ipv6_addr, ipv6_ll, 16);
+                       if (!inet_ntop(AF_INET6, &ppp->in_ipv6_addr, buf, sizeof(buf)))
+                               return -EINVAL;
                        vpn_progress(vpninfo, PRG_DEBUG,
-                                    _("Received peer IPv6 interface identifier :%x:%x:%x:%x from server\n"),
-                                    load_be16(p+2), load_be16(p+4), load_be16(p+6), load_be16(p+8));
+                                    _("Received peer IPv6 link-local address %s from server\n"),
+                                    buf);
                        break;
+               }
                default:
                unknown:
                        vpn_progress(vpninfo, PRG_DEBUG,
@@ -526,7 +539,7 @@ static int queue_config_request(struct openconnect_info *vpninfo, int proto)
                ncp = &ppp->ipcp;
 
                /* XX: send zero for IPv4/DNS/NBNS to request via NAK */
-               buf_append_ppp_tlv(buf, IPCP_IPADDR, 4, &ppp->out_peer_addr.s_addr);
+               buf_append_ppp_tlv(buf, IPCP_IPADDR, 4, &ppp->out_ipv4_addr.s_addr);
 
                /* XX: See ppp.h for why bitfields work here */
                for (int b=0; b<4; b++)
@@ -537,8 +550,12 @@ static int queue_config_request(struct openconnect_info *vpninfo, int proto)
        case PPP_IP6CP:
                ncp = &ppp->ip6cp;
 
-               /* XX: unclear if any servers care about zeros here */
-               buf_append_ppp_tlv(buf, IP6CP_INT_ID, 8, &ppp->out_ipv6_int_ident);
+               /* Send zero here if we need a link-local IPv6 address, because
+                * we don't yet have a global IPv6 address. Otherwise, just send
+                * the interface bits of our global IPv6 address, to avoid getting
+                * a CONFREQ/CONFNAK/re-CONFREQ round trip */
+               buf_append_ppp_tlv(buf, IP6CP_INT_ID, 8, ppp->out_ipv6_addr.s6_addr + 8);
+
                break;
 
        default:
@@ -626,7 +643,7 @@ static int handle_config_rejnak(struct openconnect_info *vpninfo,
                        if (code == CONFNAK && a->s_addr) {
                                vpn_progress(vpninfo, PRG_DEBUG,
                                             _("Server nak-offered IPv4 address: %s\n"), s);
-                               ppp->out_peer_addr = *a;
+                               ppp->out_ipv4_addr = *a;
                                vpninfo->ip_info.addr = strdup(s); /* XX: need free and add_option() */
                        } else {
                                vpn_progress(vpninfo, PRG_DEBUG,
@@ -663,10 +680,27 @@ static int handle_config_rejnak(struct openconnect_info *vpninfo,
                case PROTO_TAG_LEN(PPP_IP6CP, IP6CP_INT_ID, 8): {
                        uint64_t *val = (void *)(p + 2);
                        if (code == CONFNAK && *val != 0) {
+                               char buf[40];
+                               unsigned char ipv6_ll[16] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0};
+                               memcpy(ipv6_ll + 8, val, 8);
+                               if (!inet_ntop(AF_INET6, ipv6_ll, buf, sizeof(buf)))
+                                       return -EINVAL;
                                vpn_progress(vpninfo, PRG_DEBUG,
-                                            _("Server nak-offered IPv6 interface identifier\n"));
-                               memcpy(&ppp->out_ipv6_int_ident, val, 8);
-                               /* XX: unclear what we should do to make an IPv6 address from this */
+                                            _("Server nak-offered IPv6 link-local address %s\n"), buf);
+
+                               /* If we don't already have a valid global IPv6 address, then we are
+                                * supposed to use this one to create a valid link-local IPv6
+                                * address to allow autoconfiguration (https://tools.ietf.org/html/rfc5072)
+                                */
+                               if (!vpninfo->ip_info.addr6 && !vpninfo->ip_info.netmask6) {
+                                       memcpy(&ppp->out_ipv6_addr, ipv6_ll, 16);
+                                       /* XX: need add-option */
+                                       if ((asprintf((char **)&vpninfo->ip_info.netmask6, "%s/64", buf)) <= 0)
+                                               return -ENOMEM;
+                                       vpn_progress(vpninfo, PRG_INFO,
+                                                    _("Configured IPv6 link-local address %s.\n"),
+                                                    vpninfo->ip_info.netmask6);
+                               }
                        } else {
                                vpn_progress(vpninfo, PRG_DEBUG,
                                             _("Server rejected/nak'ed our IPv6 interface identifier\n"));
@@ -1052,7 +1086,7 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
                case PPP_IP6:
                        if (ppp->ppp_state != PPPS_NETWORK) {
                                vpn_progress(vpninfo, PRG_ERR,
-                                            _("Unexpected IPv%d packet in PPP state %s."),
+                                            _("Unexpected IPv%d packet in PPP state %s.\n"),
                                             (proto == PPP_IP6 ? 6 : 4), ppps_names[ppp->ppp_state]);
                                dump_buf_hex(vpninfo, PRG_ERR, '<', pp, payload_len);
                        } else {
diff --git a/ppp.h b/ppp.h
index 2f3ac6f787b31c1f32f9bde80c94533fed554d44..e6d4708cca40d50c62e2b8321d7982c7f902641f 100644 (file)
--- a/ppp.h
+++ b/ppp.h
@@ -113,8 +113,8 @@ struct oc_ppp {
        uint32_t out_asyncmap;
        int out_lcp_opts;
        int32_t out_lcp_magic; /* stored in on-the-wire order */
-       struct in_addr out_peer_addr;
-       uint64_t out_ipv6_int_ident;
+       struct in_addr out_ipv4_addr;
+       struct in6_addr out_ipv6_addr;
 
        int solicit_peerns;     /* bitfield of DNS/NBNS to request */
 
@@ -122,8 +122,8 @@ struct oc_ppp {
        uint32_t in_asyncmap;
        int in_lcp_opts;
        int32_t in_lcp_magic; /* stored in on-the-wire order */
-       struct in_addr in_peer_addr;
-       uint64_t in_ipv6_int_ident;
+       struct in_addr in_ipv4_addr;
+       struct in6_addr in_ipv6_addr;
 
        int exp_ppp_hdr_size;   /* predicted size of next PPP header */
 };