Hi,

Currently there is only one address pool which is either v4 or v6.
This means that we cannot have dual-stack VPNs via iked.  Clients
then might tunnel all IPv4 traffic, but IPv6 traffic is still using
the non-encrypted default route, which might be a security issue.
To enable dual-stack IKEv2, set up a second address pool which is
specifically for v6.

Patrick

diff --git sbin/iked/config.c sbin/iked/config.c
index 1650258..4b9b3a2 100644
--- sbin/iked/config.c
+++ sbin/iked/config.c
@@ -104,6 +104,10 @@ config_free_sa(struct iked *env, struct iked_sa *sa)
                (void)RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa);
                free(sa->sa_addrpool);
        }
+       if (sa->sa_addrpool6) {
+               (void)RB_REMOVE(iked_addrpool6, &env->sc_addrpool6, sa);
+               free(sa->sa_addrpool6);
+       }
 
        if (sa->sa_policy) {
                TAILQ_REMOVE(&sa->sa_policy->pol_sapeers, sa, sa_peer_entry);
diff --git sbin/iked/iked.h sbin/iked/iked.h
index b1c3152..3fb7c9f 100644
--- sbin/iked/iked.h
+++ sbin/iked/iked.h
@@ -449,9 +449,13 @@ struct iked_sa {
 
        struct iked_addr                *sa_addrpool;   /* address from pool */
        RB_ENTRY(iked_sa)                sa_addrpool_entry;     /* pool entries 
*/
+
+       struct iked_addr                *sa_addrpool6;  /* address from pool */
+       RB_ENTRY(iked_sa)                sa_addrpool6_entry;    /* pool entries 
*/
 };
 RB_HEAD(iked_sas, iked_sa);
 RB_HEAD(iked_addrpool, iked_sa);
+RB_HEAD(iked_addrpool6, iked_sa);
 
 struct iked_message {
        struct ibuf             *msg_data;
@@ -599,6 +603,7 @@ struct iked {
        char                            *sc_ocsp_url;
 
        struct iked_addrpool             sc_addrpool;
+       struct iked_addrpool6            sc_addrpool6;
 };
 
 struct iked_socket {
@@ -691,6 +696,7 @@ struct iked_user *
         user_lookup(struct iked *, const char *);
 RB_PROTOTYPE(iked_sas, iked_sa, sa_entry, sa_cmp);
 RB_PROTOTYPE(iked_addrpool, iked_sa, sa_addrpool_entry, sa_addrpool_cmp);
+RB_PROTOTYPE(iked_addrpool6, iked_sa, sa_addrpool6_entry, sa_addrpool6_cmp);
 RB_PROTOTYPE(iked_users, iked_user, user_entry, user_cmp);
 RB_PROTOTYPE(iked_activesas, iked_childsa, csa_node, childsa_cmp);
 RB_PROTOTYPE(iked_flows, iked_flow, flow_node, flow_cmp);
diff --git sbin/iked/ikev2.c sbin/iked/ikev2.c
index 7d36800..d4c4290 100644
--- sbin/iked/ikev2.c
+++ sbin/iked/ikev2.c
@@ -122,7 +122,7 @@ int  ikev2_add_buf(struct ibuf *buf, struct ibuf *);
 int     ikev2_ipcomp_enable(struct iked *, struct iked_sa *);
 void    ikev2_ipcomp_csa_free(struct iked *, struct iked_childsa *);
 
-int     ikev2_cp_setaddr(struct iked *, struct iked_sa *);
+int     ikev2_cp_setaddr(struct iked *, struct iked_sa *, sa_family_t);
 int     ikev2_cp_fixaddr(struct iked_sa *, struct iked_addr *,
            struct iked_addr *);
 
@@ -1739,9 +1739,9 @@ ikev2_add_cp(struct iked *env, struct iked_sa *sa, struct 
ibuf *buf)
                        in6 = (ikecfg->cfg.address.addr_mask != 128 &&
                            (ikecfg->cfg_type ==
                            IKEV2_CFG_INTERNAL_IP6_ADDRESS) &&
-                           sa->sa_addrpool &&
-                           sa->sa_addrpool->addr_af == AF_INET6) ?
-                           (struct sockaddr_in6 *)&sa->sa_addrpool->addr :
+                           sa->sa_addrpool6 &&
+                           sa->sa_addrpool6->addr_af == AF_INET6) ?
+                           (struct sockaddr_in6 *)&sa->sa_addrpool6->addr :
                            (struct sockaddr_in6 *)&ikecfg->cfg.address.addr;
                        cfg->cfg_length = htobe16(17);
                        if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1)
@@ -2184,7 +2184,8 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
        else if (!sa_stateok(sa, IKEV2_STATE_VALID))
                return (0);     /* ignore */
 
-       if (ikev2_cp_setaddr(env, sa) < 0)
+       if (ikev2_cp_setaddr(env, sa, AF_INET) < 0 ||
+           ikev2_cp_setaddr(env, sa, AF_INET6) < 0)
                return (-1);
 
        if (ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals,
@@ -2943,6 +2944,12 @@ ikev2_ikesa_enable(struct iked *env, struct iked_sa *sa, 
struct iked_sa *nsa)
                sa->sa_addrpool = NULL;
                RB_INSERT(iked_addrpool, &env->sc_addrpool, nsa);
        }
+       if (sa->sa_addrpool6) {
+               RB_REMOVE(iked_addrpool6, &env->sc_addrpool6, sa);
+               nsa->sa_addrpool6 = sa->sa_addrpool6;
+               sa->sa_addrpool6 = NULL;
+               RB_INSERT(iked_addrpool6, &env->sc_addrpool6, nsa);
+       }
 
        log_debug("%s: activating new IKE SA", __func__);
        sa_state(env, nsa, IKEV2_STATE_ESTABLISHED);
@@ -5029,7 +5036,7 @@ ikev2_print_id(struct iked_id *id, char *idstr, size_t 
idstrlen)
  * and remember it in the sa_addrpool attribute.
  */
 int
-ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa)
+ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa, sa_family_t family)
 {
        struct iked_cfg         *ikecfg = NULL;
        struct iked_policy      *pol = sa->sa_policy;
@@ -5040,18 +5047,32 @@ ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa)
        uint32_t                 mask, host, lower, upper, start;
        size_t                   i;
 
-       if (sa->sa_addrpool || pol->pol_ncfg == 0)
+       switch (family) {
+               case AF_INET:
+                       if (sa->sa_addrpool)
+                               return (0);
+                       break;
+               case AF_INET6:
+                       if (sa->sa_addrpool6)
+                               return (0);
+                       break;
+               default:
+                       return (-1);
+       }
+       if (pol->pol_ncfg == 0)
                return (0);
        /* check for an address pool config (address w/ prefixlen != 32) */
        bzero(&addr, sizeof(addr));
        for (i = 0; i < pol->pol_ncfg; i++) {
                ikecfg = &pol->pol_cfg[i];
-               if (ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS &&
+               if (family == AF_INET &&
+                   ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS &&
                    ikecfg->cfg.address.addr_mask != 32) {
                        addr.addr_af = AF_INET;
                        break;
                }
-               if (ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS &&
+               if (family == AF_INET6 &&
+                   ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS &&
                    ikecfg->cfg.address.addr_mask != 128) {
                        addr.addr_af = AF_INET6;
                        break;
@@ -5081,6 +5102,7 @@ ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa)
                in4->sin_family = AF_INET;
                in4->sin_len = sizeof(*in4);
                lower = ntohl(cfg4->sin_addr.s_addr & ~mask);
+               key.sa_addrpool = &addr;
                break;
        case AF_INET6:
                cfg6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr;
@@ -5088,6 +5110,7 @@ ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa)
                in6->sin6_family = AF_INET6;
                in6->sin6_len = sizeof(*in6);
                lower = cfg6->sin6_addr.s6_addr[3];
+               key.sa_addrpool6 = &addr;
                break;
        default:
                return (-1);
@@ -5100,8 +5123,6 @@ ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa)
        /* Randomly select start from [lower, upper-1] */
        start = arc4random_uniform(upper - lower) + lower;
 
-       key.sa_addrpool = &addr;
-
        for (host = start;;) {
                log_debug("%s: mask %x start %x lower %x host %x upper %x",
                    __func__, mask, start, lower, host, upper);
@@ -5115,7 +5136,10 @@ ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa)
                        in6->sin6_addr.s6_addr[3] = htonl(host);
                        break;
                }
-               if (!RB_FIND(iked_addrpool, &env->sc_addrpool, &key))
+               if ((addr.addr_af == AF_INET &&
+                   !RB_FIND(iked_addrpool, &env->sc_addrpool, &key)) ||
+                   (addr.addr_af == AF_INET6 &&
+                   !RB_FIND(iked_addrpool6, &env->sc_addrpool6, &key)))
                        break;
                /* try next address */
                host++;
@@ -5125,12 +5149,27 @@ ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa)
                if (host == start)
                        return (-1);            /* exhausted */
        }
-       if (!key.sa_addrpool)
-               return (-1);                    /* cannot happen? */
-       if ((sa->sa_addrpool = calloc(1, sizeof(addr))) == NULL)
+
+       switch (addr.addr_af) {
+       case AF_INET:
+               if (!key.sa_addrpool)
+                       return (-1);                    /* cannot happen? */
+               if ((sa->sa_addrpool = calloc(1, sizeof(addr))) == NULL)
+                       return (-1);
+               memcpy(sa->sa_addrpool, &addr, sizeof(addr));
+               RB_INSERT(iked_addrpool, &env->sc_addrpool, sa);
+               break;
+       case AF_INET6:
+               if (!key.sa_addrpool6)
+                       return (-1);                    /* cannot happen? */
+               if ((sa->sa_addrpool6 = calloc(1, sizeof(addr))) == NULL)
+                       return (-1);
+               memcpy(sa->sa_addrpool6, &addr, sizeof(addr));
+               RB_INSERT(iked_addrpool6, &env->sc_addrpool6, sa);
+               break;
+       default:
                return (-1);
-       memcpy(sa->sa_addrpool, &addr, sizeof(addr));
-       RB_INSERT(iked_addrpool, &env->sc_addrpool, sa);
+       }
        return (0);
 }
 
@@ -5145,21 +5184,23 @@ ikev2_cp_fixaddr(struct iked_sa *sa, struct iked_addr 
*addr,
        struct sockaddr_in      *in4;
        struct sockaddr_in6     *in6;
 
-       if (sa->sa_addrpool == NULL ||
-           sa->sa_addrpool->addr_af != addr->addr_af)
-               return (-1);
        switch (addr->addr_af) {
        case AF_INET:
+               if (sa->sa_addrpool == NULL)
+                       return (-1);
                in4 = (struct sockaddr_in *)&addr->addr;
                if (in4->sin_addr.s_addr)
                        return (-1);
+               memcpy(patched, sa->sa_addrpool, sizeof(*patched));
                break;
        case AF_INET6:
+               if (sa->sa_addrpool6 == NULL)
+                       return (-1);
                in6 = (struct sockaddr_in6 *)&addr->addr;
                if (!IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr))
                        return (-1);
+               memcpy(patched, sa->sa_addrpool6, sizeof(*patched));
                break;
        }
-       memcpy(patched, sa->sa_addrpool, sizeof(*patched));
        return (0);
 }
diff --git sbin/iked/policy.c sbin/iked/policy.c
index 0d82d78..db316dd 100644
--- sbin/iked/policy.c
+++ sbin/iked/policy.c
@@ -522,6 +522,13 @@ sa_addrpool_cmp(struct iked_sa *a, struct iked_sa *b)
            (struct sockaddr *)&b->sa_addrpool->addr, -1));
 }
 
+static __inline int
+sa_addrpool6_cmp(struct iked_sa *a, struct iked_sa *b)
+{
+       return (sockaddr_cmp((struct sockaddr *)&a->sa_addrpool6->addr,
+           (struct sockaddr *)&b->sa_addrpool6->addr, -1));
+}
+
 struct iked_user *
 user_lookup(struct iked *env, const char *user)
 {
@@ -584,6 +591,7 @@ flow_cmp(struct iked_flow *a, struct iked_flow *b)
 
 RB_GENERATE(iked_sas, iked_sa, sa_entry, sa_cmp);
 RB_GENERATE(iked_addrpool, iked_sa, sa_addrpool_entry, sa_addrpool_cmp);
+RB_GENERATE(iked_addrpool6, iked_sa, sa_addrpool6_entry, sa_addrpool6_cmp);
 RB_GENERATE(iked_users, iked_user, usr_entry, user_cmp);
 RB_GENERATE(iked_activesas, iked_childsa, csa_node, childsa_cmp);
 RB_GENERATE(iked_flows, iked_flow, flow_node, flow_cmp);

Reply via email to