On Tue, Nov 14, 2017 at 01:58:41PM +0100, Patrick Wildt wrote:
> On Thu, Nov 09, 2017 at 11:40:30AM +0100, Patrick Wildt wrote:
> > Hi,
> > 
> > this diff implements MOBIKE (RFC 4555) support in iked(8), with us
> > acting as responder.  In practice this support means that clients like
> > iPhones can roam in different networks (LTE, WiFi) and change their
> > external address without having to re-do the whole handshake.  It allows
> > the client to choose how and when to change the external tunnel endpoint
> > addresses on demand, depending on which network is better or even is
> > connected at all.
> > 
> > This diff already had a few iterations where race conditions were found
> > and ironed out, so this should be a rather stable version which has been
> > working really well for me now.
> > 
> > Key changes with this diff are:
> > 
> >  * MOBIKE is on by default, but can be turned off (if neccessary)
> >    using a config key.
> >  * MOBIKE support is then announced to the initiator.
> >  * Peers can use different IP addresses in the IKE messages, but the
> >    Tunnel is only updated when we receive IKEV2_N_UPDATE_SA_ADDRESSES.
> >  * This means we need to store two peer addresses.  The one he talked to us
> >    last, so that we know where to respond to.  And the one that the kernel
> >    knows, so that we know if we have to update the SAs and flows.
> >  * We need to update the SA as well as the flow.  For the SAs I added
> >    a way of updating destination addresses of an existing TDB in June.
> >    The flows simply need a reload after the SA changed.
> >  * COOKIE2 is used for return routability checks.  strongswan seems
> >    to use it.  It's encrypted and should be sent back to the initiator.
> > 
> > When you, for instance, use your phone and turn on/off WiFi, you should
> > see that the VPN stays connected and iked should log that the address
> > has changed.  Especially `ipsecctl -sa` should show updated SAs and
> > flows.
> > 
> > OKs? Objections? Feedback?
> 
> I have heard from two more people running it and seeing it work.  Anyone
> else?

So far the response has been good.  Any OKs or should I just put it in
to give it more exposure?

Patrick

> > Patrick
> > 
> > diff --git a/sbin/iked/config.c b/sbin/iked/config.c
> > index 590e4d7f4da..da8d0745f3c 100644
> > --- a/sbin/iked/config.c
> > +++ b/sbin/iked/config.c
> > @@ -814,6 +814,29 @@ config_getcompile(struct iked *env, struct imsg *imsg)
> >     return (0);
> >  }
> >  
> > +int
> > +config_setmobike(struct iked *env)
> > +{
> > +   unsigned int boolval;
> > +
> > +   boolval = env->sc_mobike;
> > +   proc_compose(&env->sc_ps, PROC_IKEV2, IMSG_CTL_MOBIKE,
> > +       &boolval, sizeof(boolval));
> > +   return (0);
> > +}
> > +
> > +int
> > +config_getmobike(struct iked *env, struct imsg *imsg)
> > +{
> > +   unsigned int boolval;
> > +
> > +   IMSG_SIZE_CHECK(imsg, &boolval);
> > +   memcpy(&boolval, imsg->data, sizeof(boolval));
> > +   env->sc_mobike = boolval;
> > +   log_debug("%s: %smobike", __func__, env->sc_mobike ? "" : "no ");
> > +   return (0);
> > +}
> > +
> >  int
> >  config_setocsp(struct iked *env)
> >  {
> > diff --git a/sbin/iked/iked.c b/sbin/iked/iked.c
> > index 09fef3ea877..548da77d014 100644
> > --- a/sbin/iked/iked.c
> > +++ b/sbin/iked/iked.c
> > @@ -250,6 +250,7 @@ parent_configure(struct iked *env)
> >     if (pledge("stdio rpath proc dns inet route sendfd", NULL) == -1)
> >             fatal("pledge");
> >  
> > +   config_setmobike(env);
> >     config_setcoupled(env, env->sc_decoupled ? 0 : 1);
> >     config_setmode(env, env->sc_passive ? 1 : 0);
> >     config_setocsp(env);
> > @@ -280,6 +281,7 @@ parent_reload(struct iked *env, int reset, const char 
> > *filename)
> >             /* Re-compile policies and skip steps */
> >             config_setcompile(env, PROC_IKEV2);
> >  
> > +           config_setmobike(env);
> >             config_setcoupled(env, env->sc_decoupled ? 0 : 1);
> >             config_setmode(env, env->sc_passive ? 1 : 0);
> >             config_setocsp(env);
> > diff --git a/sbin/iked/iked.conf.5 b/sbin/iked/iked.conf.5
> > index 8c77f24d603..be0e8ef30f6 100644
> > --- a/sbin/iked/iked.conf.5
> > +++ b/sbin/iked/iked.conf.5
> > @@ -136,6 +136,15 @@ This is the default.
> >  .It Ic set decouple
> >  Don't load the negotiated SAs and flows from the kernel.
> >  This mode is only useful for testing and debugging.
> > +.It Ic set mobike
> > +Enable MOBIKE (RFC 4555) support.
> > +This is the default.
> > +MOBIKE allows the change of the peer IP address for IKE and IPsec SAs.
> > +Currenly
> > +.Xr iked 8
> > +only supports MOBIKE when acting as a responder.
> > +.It Ic set nomobike
> > +Disables MOBIKE support.
> >  .It Ic set ocsp Ar URL
> >  Enable OCSP and set the URL of the OCSP responder.
> >  Please note that the matching responder and issuer certificates
> > diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h
> > index b536d58e157..c2030ea7460 100644
> > --- a/sbin/iked/iked.h
> > +++ b/sbin/iked/iked.h
> > @@ -157,6 +157,8 @@ struct iked_flow {
> >  
> >     RB_ENTRY(iked_flow)              flow_node;
> >     TAILQ_ENTRY(iked_flow)           flow_entry;
> > +
> > +   int                              flow_replacing; /* cf flow_replace() */
> >     int                              flow_ipcomp;
> >  };
> >  RB_HEAD(iked_flows, iked_flow);
> > @@ -371,6 +373,7 @@ struct iked_sa {
> >  #define IKED_SATYPE_LOCAL           1              /* Local SA */
> >  
> >     struct iked_addr                 sa_peer;
> > +   struct iked_addr                 sa_peer_loaded;/* MOBIKE */
> >     struct iked_addr                 sa_local;
> >     int                              sa_fd;
> >  
> > @@ -441,6 +444,8 @@ struct iked_sa {
> >     uint16_t                         sa_cpi_out;    /* IPcomp outgoing */
> >     uint16_t                         sa_cpi_in;     /* IPcomp incoming*/
> >  
> > +   int                              sa_mobike;     /* MOBIKE */
> > +
> >     struct iked_timer                sa_timer;      /* SA timeouts */
> >  #define IKED_IKE_SA_EXCHANGE_TIMEOUT        300            /* 5 minutes */
> >  #define IKED_IKE_SA_REKEY_TIMEOUT   120            /* 2 minutes */
> > @@ -487,6 +492,7 @@ struct iked_message {
> >     int                      msg_response;
> >     int                      msg_responded;
> >     int                      msg_natt;
> > +   int                      msg_natt_rcvd;
> >     int                      msg_error;
> >     int                      msg_e;
> >     struct iked_message     *msg_parent;
> > @@ -508,6 +514,10 @@ struct iked_message {
> >     struct iked_id           msg_cert;
> >     struct ibuf             *msg_cookie;
> >  
> > +   /* MOBIKE */
> > +   int                      msg_update_sa_addresses;
> > +   struct ibuf             *msg_cookie2;
> > +
> >     /* Parse stack */
> >     struct iked_proposal    *msg_prop;
> >     uint16_t                 msg_attrlength;
> > @@ -590,6 +600,8 @@ struct iked {
> >     uint8_t                          sc_passive;
> >     uint8_t                          sc_decoupled;
> >  
> > +   uint8_t                          sc_mobike;     /* MOBIKE */
> > +
> >     struct iked_policies             sc_policies;
> >     struct iked_policy              *sc_defaultcon;
> >  
> > @@ -687,6 +699,8 @@ int      config_setocsp(struct iked *);
> >  int         config_getocsp(struct iked *, struct imsg *);
> >  int         config_setkeys(struct iked *);
> >  int         config_getkey(struct iked *, struct imsg *);
> > +int         config_setmobike(struct iked *);
> > +int         config_getmobike(struct iked *, struct imsg *);
> >  
> >  /* policy.c */
> >  void        policy_init(struct iked *);
> > @@ -711,6 +725,7 @@ struct iked_childsa *
> >      childsa_lookup(struct iked_sa *, uint64_t, uint8_t);
> >  void        flow_free(struct iked_flow *);
> >  int         flow_equal(struct iked_flow *, struct iked_flow *);
> > +int         flow_replace(struct iked *, struct iked_flow *);
> >  struct iked_sa *
> >      sa_lookup(struct iked *, uint64_t, uint64_t, unsigned int);
> >  struct iked_user *
> > @@ -860,6 +875,7 @@ int      pfkey_flow_delete(int fd, struct iked_flow *);
> >  int         pfkey_block(int, int, unsigned int);
> >  int         pfkey_sa_init(int, struct iked_childsa *, uint32_t *);
> >  int         pfkey_sa_add(int, struct iked_childsa *, struct iked_childsa 
> > *);
> > +int         pfkey_sa_update_addresses(int, struct iked_childsa *);
> >  int         pfkey_sa_delete(int, struct iked_childsa *);
> >  int         pfkey_sa_last_used(int, struct iked_childsa *, uint64_t *);
> >  int         pfkey_flush(int);
> > diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
> > index e908b6230a5..ffbea0eccc3 100644
> > --- a/sbin/iked/ikev2.c
> > +++ b/sbin/iked/ikev2.c
> > @@ -140,6 +140,13 @@ ssize_t        ikev2_add_sighashnotify(struct ibuf *, 
> > struct ikev2_payload **,
> >  ssize_t ikev2_add_nat_detection(struct iked *, struct ibuf *,
> >         struct ikev2_payload **, struct iked_message *, ssize_t);
> >  
> > +ssize_t     ikev2_add_mobike(struct iked *, struct ibuf *,
> > +       struct ikev2_payload **, ssize_t, struct iked_sa *);
> > +int         ikev2_update_sa_addresses(struct iked *, struct iked_sa *);
> > +int         ikev2_resp_informational(struct iked *, struct iked_sa *,
> > +       struct iked_message *);
> > +
> > +
> >  static struct privsep_proc procs[] = {
> >     { "parent",     PROC_PARENT,    ikev2_dispatch_parent },
> >     { "certstore",  PROC_CERT,      ikev2_dispatch_cert }
> > @@ -189,6 +196,8 @@ ikev2_dispatch_parent(int fd, struct privsep_proc *p, 
> > struct imsg *imsg)
> >                         IKED_INITIATOR_INITIAL);
> >             }
> >             return (0);
> > +   case IMSG_CTL_MOBIKE:
> > +           return (config_getmobike(env, imsg));
> >     case IMSG_UDP_SOCKET:
> >             return (config_getsocket(env, imsg, ikev2_msg_cb));
> >     case IMSG_PFKEY_SOCKET:
> > @@ -1584,6 +1593,30 @@ ikev2_add_ipcompnotify(struct iked *env, struct ibuf 
> > *e,
> >     return (len);
> >  }
> >  
> > +ssize_t
> > +ikev2_add_mobike(struct iked *env, struct ibuf *e,
> > +    struct ikev2_payload **pld, ssize_t len, struct iked_sa *sa)
> > +{
> > +   struct ikev2_notify             *n;
> > +   uint8_t                         *ptr;
> > +
> > +   if (*pld)
> > +           if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1)
> > +                   return (-1);
> > +   if ((*pld = ikev2_add_payload(e)) == NULL)
> > +           return (-1);
> > +   len = sizeof(*n);
> > +   if ((ptr = ibuf_advance(e, len)) == NULL)
> > +           return (-1);
> > +   n = (struct ikev2_notify *)ptr;
> > +   n->n_protoid = 0;
> > +   n->n_spisize = 0;
> > +   n->n_type = htobe16(IKEV2_N_MOBIKE_SUPPORTED);
> > +   log_debug("%s: done", __func__);
> > +
> > +   return (len);
> > +}
> > +
> >  ssize_t
> >  ikev2_add_sighashnotify(struct ibuf *e, struct ikev2_payload **pld,
> >      ssize_t len)
> > @@ -2055,6 +2088,84 @@ ikev2_add_buf(struct ibuf *buf, struct ibuf *data)
> >     return (0);
> >  }
> >  
> > +int
> > +ikev2_resp_informational(struct iked *env, struct iked_sa *sa,
> > +    struct iked_message *msg)
> > +{
> > +   struct ikev2_notify             *n;
> > +   struct ikev2_payload            *pld = NULL;
> > +   struct ike_header               *hdr;
> > +   struct ibuf                     *buf = NULL;
> > +   ssize_t                          len = 0;
> > +   int                              ret = -1;
> > +   int                              oflags = 0;
> > +   uint8_t                          firstpayload = IKEV2_PAYLOAD_NONE;
> > +
> > +   if (!sa_stateok(sa, IKEV2_STATE_AUTH_REQUEST) ||
> > +       msg->msg_responded || msg->msg_error)
> > +           goto done;
> > +
> > +   if ((buf = ibuf_static()) == NULL)
> > +           goto done;
> > +   /*
> > +    * Include NAT_DETECTION notification on UPDATE_SA_ADDRESSES or if
> > +    * the peer did include them, too (RFC 455, 3.8).
> > +    */
> > +   if (sa->sa_mobike &&
> > +       (msg->msg_update_sa_addresses || msg->msg_natt_rcvd)) {
> > +           /*
> > +            * XXX workaround so ikev2_msg_frompeer() fails for
> > +            * XXX ikev2_nat_detection(), and the correct src/dst are
> > +            * XXX used for the nat detection payload.
> > +            */
> > +           if (msg->msg_parent == NULL)
> > +                   goto done;
> > +           if ((hdr = ibuf_seek(msg->msg_parent->msg_data, 0,
> > +               sizeof(*hdr))) == NULL)
> > +                   goto done;
> > +           oflags = hdr->ike_flags;
> > +           if (sa->sa_hdr.sh_initiator)
> > +                   hdr->ike_flags |= IKEV2_FLAG_INITIATOR;
> > +           else
> > +                   hdr->ike_flags &= ~IKEV2_FLAG_INITIATOR;
> > +           /* NAT-T notify payloads */
> > +           len = ikev2_add_nat_detection(env, buf, &pld, msg, len);
> > +           hdr->ike_flags = oflags;        /* XXX undo workaround */
> > +           if (len == -1)
> > +                   goto done;
> > +           firstpayload = IKEV2_PAYLOAD_NOTIFY;
> > +   }
> > +   /* Reflect COOKIE2 */
> > +   if (msg->msg_cookie2) {
> > +           if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NOTIFY) == -1)
> > +                   goto done;
> > +           if ((pld = ikev2_add_payload(buf)) == NULL)
> > +                   goto done;
> > +           if ((n = ibuf_advance(buf, sizeof(*n))) == NULL)
> > +                   goto done;
> > +           n->n_protoid = IKEV2_SAPROTO_IKE;
> > +           n->n_spisize = 0;
> > +           n->n_type = htobe16(IKEV2_N_COOKIE2);
> > +           if (ikev2_add_buf(buf, msg->msg_cookie2) == -1)
> > +                   goto done;
> > +           len = sizeof(*n) + ibuf_size(msg->msg_cookie2);
> > +           log_debug("%s: added cookie2", __func__);
> > +           if (firstpayload == IKEV2_PAYLOAD_NONE)
> > +                   firstpayload = IKEV2_PAYLOAD_NOTIFY;
> > +   }
> > +   /* add terminator, if there is already a payload */
> > +   if (firstpayload != IKEV2_PAYLOAD_NONE)
> > +           if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1)
> > +                   goto done;
> > +   ret = ikev2_msg_send_encrypt(env, sa, &buf,
> > +       IKEV2_EXCHANGE_INFORMATIONAL, firstpayload, 1);
> > +   if (ret != -1)
> > +           msg->msg_responded = 1;
> > + done:
> > +   ibuf_release(buf);
> > +   return (ret);
> > +}
> > +
> >  void
> >  ikev2_resp_recv(struct iked *env, struct iked_message *msg,
> >      struct ike_header *hdr)
> > @@ -2152,13 +2263,9 @@ ikev2_resp_recv(struct iked *env, struct 
> > iked_message *msg,
> >                     ikev2_send_error(env, sa, msg, hdr->ike_exchange);
> >             break;
> >     case IKEV2_EXCHANGE_INFORMATIONAL:
> > -           if (sa_stateok(sa, IKEV2_STATE_AUTH_REQUEST) &&
> > -               !msg->msg_responded && !msg->msg_error) {
> > -                   (void)ikev2_send_ike_e(env, sa, NULL,
> > -                       IKEV2_PAYLOAD_NONE, IKEV2_EXCHANGE_INFORMATIONAL,
> > -                       1);
> > -                   msg->msg_responded = 1;
> > -           }
> > +           if (msg->msg_update_sa_addresses)
> > +                   ikev2_update_sa_addresses(env, sa);
> > +           (void)ikev2_resp_informational(env, sa, msg);
> >             break;
> >     default:
> >             break;
> > @@ -2451,6 +2558,11 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa 
> > *sa)
> >         (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa)) == -1)
> >             goto done;
> >  
> > +   /* MOBIKE */
> > +   if (sa->sa_mobike &&
> > +       (len = ikev2_add_mobike(env, e, &pld, len, sa)) == -1)
> > +           goto done;
> > +
> >     if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1)
> >             goto done;
> >  
> > @@ -3151,10 +3263,13 @@ ikev2_ikesa_enable(struct iked *env, struct iked_sa 
> > *sa, struct iked_sa *nsa)
> >     nsa->sa_natt = sa->sa_natt;
> >     nsa->sa_udpencap = sa->sa_udpencap;
> >     nsa->sa_usekeepalive = sa->sa_usekeepalive;
> > +   nsa->sa_mobike = sa->sa_mobike;
> >  
> >     /* Transfer old addresses */
> >     memcpy(&nsa->sa_local, &sa->sa_local, sizeof(nsa->sa_local));
> >     memcpy(&nsa->sa_peer, &sa->sa_peer, sizeof(nsa->sa_peer));
> > +   memcpy(&nsa->sa_peer_loaded, &sa->sa_peer_loaded,
> > +       sizeof(nsa->sa_peer_loaded));
> >  
> >     /* Transfer all Child SAs and flows from the old IKE SA */
> >     for (flow = TAILQ_FIRST(&sa->sa_flows); flow != NULL;
> > @@ -5104,6 +5219,7 @@ ikev2_childsa_enable(struct iked *env, struct iked_sa 
> > *sa)
> >  {
> >     struct iked_childsa     *csa, *ocsa;
> >     struct iked_flow        *flow, *oflow;
> > +   int                      peer_changed, reload;
> >  
> >     if (sa->sa_ipcomp && sa->sa_cpi_in && sa->sa_cpi_out &&
> >         ikev2_ipcomp_enable(env, sa) == -1)
> > @@ -5136,9 +5252,26 @@ ikev2_childsa_enable(struct iked *env, struct 
> > iked_sa *sa)
> >                 print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size));
> >     }
> >  
> > +   peer_changed = (memcmp(&sa->sa_peer_loaded, &sa->sa_peer,
> > +       sizeof(sa->sa_peer_loaded)) != 0);
> > +
> >     TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
> > -           if (flow->flow_loaded)
> > -                   continue;
> > +           /* re-load the flow if the peer for the flow has changed */
> > +           reload = 0;
> > +           if (flow->flow_loaded) {
> > +                   if (!peer_changed) {
> > +                           log_debug("%s: flow already loaded %p",
> > +                               __func__, flow);
> > +                           continue;
> > +                   }
> > +                   RB_REMOVE(iked_flows, &env->sc_activeflows, flow);
> > +                   /* flow might be shared w/other SA */
> > +                   if (!flow->flow_replacing ||
> > +                       flow_replace(env, flow) != 0)
> > +                           (void)pfkey_flow_delete(env->sc_pfkey, flow);
> > +                   flow->flow_loaded = 0; /* we did RB_REMOVE */
> > +                   reload = 1;
> > +           }
> >  
> >             if (pfkey_flow_add(env->sc_pfkey, flow) != 0) {
> >                     log_debug("%s: failed to load flow", __func__);
> > @@ -5150,12 +5283,24 @@ ikev2_childsa_enable(struct iked *env, struct 
> > iked_sa *sa)
> >                     log_debug("%s: replaced old flow %p with %p",
> >                         __func__, oflow, flow);
> >                     oflow->flow_loaded = 0;
> > +                   oflow->flow_replacing = 0;
> >                     RB_REMOVE(iked_flows, &env->sc_activeflows, oflow);
> > +                   flow->flow_replacing = 1;
> >             }
> >  
> >             RB_INSERT(iked_flows, &env->sc_activeflows, flow);
> >  
> > -           log_debug("%s: loaded flow %p", __func__, flow);
> > +           log_debug("%s: %sloaded flow %p", __func__,
> > +               reload ? "re" : "", flow);
> > +   }
> > +
> > +   /* remember the current address for ikev2_update_sa_addresses()  */
> > +   if (peer_changed) {
> > +           memcpy(&sa->sa_peer_loaded, &sa->sa_peer,
> > +               sizeof(sa->sa_peer_loaded));
> > +           log_debug("%s: remember SA peer %s", __func__,
> > +               print_host((struct sockaddr *)&sa->sa_peer_loaded.addr,
> > +               NULL, 0));
> >     }
> >  
> >     return (0);
> > @@ -5721,3 +5866,81 @@ ikev2_cp_fixaddr(struct iked_sa *sa, struct 
> > iked_addr *addr,
> >     }
> >     return (0);
> >  }
> > +
> > +int
> > +ikev2_update_sa_addresses(struct iked *env, struct iked_sa *sa)
> > +{
> > +   struct iked_childsa     *csa;
> > +   struct iked_flow        *flow, *oflow;
> > +   struct iked_message     *msg;
> > +
> > +   if (!sa_stateok(sa, IKEV2_STATE_ESTABLISHED))
> > +           return -1;
> > +
> > +   log_info("%s: old %s new %s", __func__,
> > +       print_host((struct sockaddr *)&sa->sa_peer_loaded.addr, NULL, 0),
> > +       print_host((struct sockaddr *)&sa->sa_peer.addr, NULL, 0));
> > +
> > +   TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
> > +           if (!csa->csa_loaded)
> > +                   continue;
> > +           if (pfkey_sa_update_addresses(env->sc_pfkey, csa) != 0)
> > +                   log_debug("%s: failed to update sa", __func__);
> > +   }
> > +
> > +   /* delete and re-add flows */
> > +   TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
> > +           if (flow->flow_loaded) {
> > +                   RB_REMOVE(iked_flows, &env->sc_activeflows, flow);
> > +                   /* flow might be shared w/other SA */
> > +                   if (!flow->flow_replacing ||
> > +                       flow_replace(env, flow) != 0)
> > +                           (void)pfkey_flow_delete(env->sc_pfkey, flow);
> > +                   flow->flow_loaded = 0;
> > +           }
> > +           /* update IPcomp flows */
> > +           if (flow->flow_ipcomp) {
> > +                   struct iked_addr *addr =
> > +                       (flow->flow_dir == IPSP_DIRECTION_OUT) ?
> > +                       &flow->flow_dst :
> > +                       &flow->flow_src;
> > +                   memcpy(addr, &sa->sa_peer, sizeof(sa->sa_peer));
> > +                   socket_setport((struct sockaddr *)&addr->addr, 0);
> > +                   addr->addr_port = 0;
> > +                   addr->addr_mask = (addr->addr_af == AF_INET) ? 32 : 128;
> > +           }
> > +           if (pfkey_flow_add(env->sc_pfkey, flow) != 0)
> > +                   log_debug("%s: failed to add flow %p", __func__, flow);
> > +           if (!flow->flow_loaded)
> > +                   continue;
> > +           if ((oflow = RB_FIND(iked_flows, &env->sc_activeflows, flow))
> > +               != NULL) {
> > +                   log_debug("%s: replaced old flow %p with %p",
> > +                       __func__, oflow, flow);
> > +                   oflow->flow_loaded = 0;
> > +                   oflow->flow_replacing = 0;
> > +                   RB_REMOVE(iked_flows, &env->sc_activeflows, oflow);
> > +                   flow->flow_replacing = 1;
> > +           }
> > +           RB_INSERT(iked_flows, &env->sc_activeflows, flow);
> > +   }
> > +
> > +   /* update pending requests and responses */
> > +   TAILQ_FOREACH(msg, &sa->sa_requests, msg_entry) {
> > +           msg->msg_local = sa->sa_local.addr;
> > +           msg->msg_locallen = sa->sa_local.addr.ss_len;
> > +           msg->msg_peer = sa->sa_peer.addr;
> > +           msg->msg_peerlen = sa->sa_peer.addr.ss_len;
> > +   }
> > +   TAILQ_FOREACH(msg, &sa->sa_responses, msg_entry) {
> > +           msg->msg_local = sa->sa_local.addr;
> > +           msg->msg_locallen = sa->sa_local.addr.ss_len;
> > +           msg->msg_peer = sa->sa_peer.addr;
> > +           msg->msg_peerlen = sa->sa_peer.addr.ss_len;
> > +   }
> > +
> > +   /* Update sa_peer_loaded, to match in-kernel information */
> > +   memcpy(&sa->sa_peer_loaded, &sa->sa_peer, sizeof(sa->sa_peer_loaded));
> > +
> > +   return 0;
> > +}
> > diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c
> > index 79c96316946..c94808fe773 100644
> > --- a/sbin/iked/ikev2_msg.c
> > +++ b/sbin/iked/ikev2_msg.c
> > @@ -184,6 +184,7 @@ ikev2_msg_cleanup(struct iked *env, struct iked_message 
> > *msg)
> >             ibuf_release(msg->msg_id.id_buf);
> >             ibuf_release(msg->msg_cert.id_buf);
> >             ibuf_release(msg->msg_cookie);
> > +           ibuf_release(msg->msg_cookie2);
> >  
> >             msg->msg_nonce = NULL;
> >             msg->msg_ke = NULL;
> > @@ -191,6 +192,7 @@ ikev2_msg_cleanup(struct iked *env, struct iked_message 
> > *msg)
> >             msg->msg_id.id_buf = NULL;
> >             msg->msg_cert.id_buf = NULL;
> >             msg->msg_cookie = NULL;
> > +           msg->msg_cookie2 = NULL;
> >  
> >             config_free_proposals(&msg->msg_proposals, 0);
> >     }
> > diff --git a/sbin/iked/ikev2_pld.c b/sbin/iked/ikev2_pld.c
> > index 5724520f696..6ccae7b6654 100644
> > --- a/sbin/iked/ikev2_pld.c
> > +++ b/sbin/iked/ikev2_pld.c
> > @@ -1153,6 +1153,8 @@ ikev2_pld_notify(struct iked *env, struct 
> > ikev2_payload *pld,
> >                             msg->msg_sa->sa_usekeepalive = 1;
> >             }
> >             print_hex(md, 0, sizeof(md));
> > +           /* remember for MOBIKE */
> > +           msg->msg_parent->msg_natt_rcvd = 1;
> >             break;
> >     case IKEV2_N_AUTHENTICATION_FAILED:
> >             if (!msg->msg_e) {
> > @@ -1292,6 +1294,66 @@ ikev2_pld_notify(struct iked *env, struct 
> > ikev2_payload *pld,
> >                     msg->msg_sa->sa_cpi_out = betoh16(cpi);
> >             }
> >             break;
> > +   case IKEV2_N_MOBIKE_SUPPORTED:
> > +           if (!msg->msg_e) {
> > +                   log_debug("%s: N_MOBIKE_SUPPORTED not encrypted",
> > +                       __func__);
> > +                   return (-1);
> > +           }
> > +           if (len != 0) {
> > +                   log_debug("%s: ignoring malformed mobike"
> > +                       " notification: %zu", __func__, len);
> > +                   return (0);
> > +           }
> > +           if (!env->sc_mobike) {
> > +                   log_debug("%s: mobike disabled", __func__);
> > +                   return (0);
> > +           }
> > +           msg->msg_sa->sa_mobike = 1;
> > +           /* enforce natt */
> > +           msg->msg_sa->sa_natt = 1;
> > +           break;
> > +   case IKEV2_N_UPDATE_SA_ADDRESSES:
> > +           if (!msg->msg_e) {
> > +                   log_debug("%s: N_UPDATE_SA_ADDRESSES not encrypted",
> > +                       __func__);
> > +                   return (-1);
> > +           }
> > +           if (!msg->msg_sa->sa_mobike) {
> > +                   log_debug("%s: ignoring update sa addresses"
> > +                       " notification w/o mobike: %zu", __func__, len);
> > +                   return (0);
> > +           }
> > +           if (len != 0) {
> > +                   log_debug("%s: ignoring malformed update sa addresses"
> > +                       " notification: %zu", __func__, len);
> > +                   return (0);
> > +           }
> > +           msg->msg_parent->msg_update_sa_addresses = 1;
> > +           break;
> > +   case IKEV2_N_COOKIE2:
> > +           if (!msg->msg_e) {
> > +                   log_debug("%s: N_COOKIE2 not encrypted",
> > +                       __func__);
> > +                   return (-1);
> > +           }
> > +           if (!msg->msg_sa->sa_mobike) {
> > +                   log_debug("%s: ignoring cookie2 notification"
> > +                       " w/o mobike: %zu", __func__, len);
> > +                   return (0);
> > +           }
> > +           if (len < IKED_COOKIE2_MIN || len > IKED_COOKIE2_MAX) {
> > +                   log_debug("%s: ignoring malformed cookie2"
> > +                       " notification: %zu", __func__, len);
> > +                   return (0);
> > +           }
> > +           ibuf_release(msg->msg_cookie2); /* should not happen */
> > +           if ((msg->msg_cookie2 = ibuf_new(buf, len)) == NULL) {
> > +                   log_debug("%s: failed to get peer cookie2", __func__);
> > +                   return (-1);
> > +           }
> > +           msg->msg_parent->msg_cookie2 = msg->msg_cookie2;
> > +           break;
> >     case IKEV2_N_COOKIE:
> >             if (msg->msg_e) {
> >                     log_debug("%s: N_COOKIE encrypted",
> > diff --git a/sbin/iked/parse.y b/sbin/iked/parse.y
> > index 419a5996f36..708165a7808 100644
> > --- a/sbin/iked/parse.y
> > +++ b/sbin/iked/parse.y
> > @@ -99,6 +99,7 @@ static int                 debug = 0;
> >  static int          rules = 0;
> >  static int          passive = 0;
> >  static int          decouple = 0;
> > +static int          mobike = 1;
> >  static char                *ocsp_url = NULL;
> >  
> >  struct ipsec_xf {
> > @@ -384,7 +385,7 @@ typedef struct {
> >  %token     PASSIVE ACTIVE ANY TAG TAP PROTO LOCAL GROUP NAME CONFIG EAP 
> > USER
> >  %token     IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET
> >  %token     INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT
> > -%token     IPCOMP OCSP IKELIFETIME
> > +%token     IPCOMP OCSP IKELIFETIME MOBIKE NOMOBIKE
> >  %token     <v.string>              STRING
> >  %token     <v.number>              NUMBER
> >  %type      <v.string>              string
> > @@ -445,6 +446,8 @@ set             : SET ACTIVE    { passive = 0; }
> >             | SET PASSIVE   { passive = 1; }
> >             | SET COUPLE    { decouple = 0; }
> >             | SET DECOUPLE  { decouple = 1; }
> > +           | SET MOBIKE    { mobike = 1; }
> > +           | SET NOMOBIKE  { mobike = 0; }
> >             | SET OCSP STRING               {
> >                     if ((ocsp_url = strdup($3)) == NULL) {
> >                             yyerror("cannot set ocsp_url");
> > @@ -1126,7 +1129,9 @@ lookup(char *s)
> >             { "ipcomp",             IPCOMP },
> >             { "lifetime",           LIFETIME },
> >             { "local",              LOCAL },
> > +           { "mobike",             MOBIKE },
> >             { "name",               NAME },
> > +           { "nomobike",           NOMOBIKE },
> >             { "ocsp",               OCSP },
> >             { "passive",            PASSIVE },
> >             { "peer",               PEER },
> > @@ -1495,6 +1500,7 @@ parse_config(const char *filename, struct iked *x_env)
> >             return (-1);
> >  
> >     decouple = passive = 0;
> > +   mobike = 1;
> >  
> >     if (env->sc_opts & IKED_OPT_PASSIVE)
> >             passive = 1;
> > @@ -1505,6 +1511,7 @@ parse_config(const char *filename, struct iked *x_env)
> >  
> >     env->sc_passive = passive ? 1 : 0;
> >     env->sc_decoupled = decouple ? 1 : 0;
> > +   env->sc_mobike = mobike;
> >     env->sc_ocsp_url = ocsp_url;
> >  
> >     if (!rules)
> > diff --git a/sbin/iked/pfkey.c b/sbin/iked/pfkey.c
> > index bcee41e29f3..9720bac174f 100644
> > --- a/sbin/iked/pfkey.c
> > +++ b/sbin/iked/pfkey.c
> > @@ -45,6 +45,9 @@
> >  #define PFKEYV2_CHUNK sizeof(uint64_t)
> >  #define PFKEY_REPLY_TIMEOUT 1000
> >  
> > +/* only used internally */
> > +#define IKED_SADB_UPDATE_SA_ADDRESSES 0xff
> > +
> >  static uint32_t sadb_msg_seq = 0;
> >  static unsigned int sadb_decoupled = 0;
> >  static unsigned int sadb_ipv6refcnt = 0;
> > @@ -435,17 +438,18 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, 
> > struct iked_childsa *sa)
> >  {
> >     struct sadb_msg          smsg;
> >     struct sadb_sa           sadb;
> > -   struct sadb_address      sa_src, sa_dst;
> > +   struct sadb_address      sa_src, sa_dst, sa_pxy;
> >     struct sadb_key          sa_authkey, sa_enckey;
> >     struct sadb_lifetime     sa_ltime_hard, sa_ltime_soft;
> >     struct sadb_x_udpencap   udpencap;
> >     struct sadb_x_tag        sa_tag;
> >     char                    *tag = NULL;
> >     struct sadb_x_tap        sa_tap;
> > -   struct sockaddr_storage  ssrc, sdst;
> > +   struct sockaddr_storage  ssrc, sdst, spxy;
> >     struct sadb_ident       *sa_srcid, *sa_dstid;
> >     struct iked_lifetime    *lt;
> >     struct iked_policy      *pol;
> > +   struct iked_addr        *dst;
> >     struct iovec             iov[IOV_CNT];
> >     uint32_t                 jitter;
> >     int                      iov_cnt;
> > @@ -467,13 +471,26 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, 
> > struct iked_childsa *sa)
> >             return (-1);
> >     }
> >  
> > +   dst = (action == IKED_SADB_UPDATE_SA_ADDRESSES &&
> > +       sa->csa_dir == IPSP_DIRECTION_OUT) ?
> > +       &sa->csa_ikesa->sa_peer_loaded :
> > +       sa->csa_peer;
> >     bzero(&sdst, sizeof(sdst));
> > -   memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
> > +   memcpy(&sdst, &dst->addr, sizeof(sdst));
> >     if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
> >             log_warn("%s: invalid address", __func__);
> >             return (-1);
> >     }
> >  
> > +   bzero(&spxy, sizeof(spxy));
> > +   if (dst != sa->csa_peer) {
> > +           memcpy(&spxy, &sa->csa_peer->addr, sizeof(spxy));
> > +           if (socket_af((struct sockaddr *)&spxy, 0) == -1) {
> > +                   log_warn("%s: invalid address", __func__);
> > +                   return (-1);
> > +           }
> > +   }
> > +
> >     bzero(&smsg, sizeof(smsg));
> >     smsg.sadb_msg_version = PF_KEY_V2;
> >     smsg.sadb_msg_seq = ++sadb_msg_seq;
> > @@ -503,6 +520,10 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, 
> > struct iked_childsa *sa)
> >     sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
> >     sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
> >  
> > +   bzero(&sa_pxy, sizeof(sa_pxy));
> > +   sa_pxy.sadb_address_len = (sizeof(sa_pxy) + ROUNDUP(spxy.ss_len)) / 8;
> > +   sa_pxy.sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
> > +
> >     bzero(&sa_authkey, sizeof(sa_authkey));
> >     bzero(&sa_enckey, sizeof(sa_enckey));
> >     bzero(&udpencap, sizeof udpencap);
> > @@ -524,6 +545,11 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, 
> > struct iked_childsa *sa)
> >                 ntohs(udpencap.sadb_x_udpencap_port));
> >     }
> >  
> > +   if (action == IKED_SADB_UPDATE_SA_ADDRESSES) {
> > +           smsg.sadb_msg_type = SADB_UPDATE;
> > +           goto send;
> > +   }
> > +
> >     if ((action == SADB_ADD || action == SADB_UPDATE) &&
> >         !sa->csa_persistent && (lt->lt_bytes || lt->lt_seconds)) {
> >             sa_ltime_hard.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
> > @@ -655,6 +681,17 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, 
> > struct iked_childsa *sa)
> >     smsg.sadb_msg_len += sa_dst.sadb_address_len;
> >     iov_cnt++;
> >  
> > +   if (spxy.ss_len) {
> > +           /* pxy addr */
> > +           iov[iov_cnt].iov_base = &sa_pxy;
> > +           iov[iov_cnt].iov_len = sizeof(sa_pxy);
> > +           iov_cnt++;
> > +           iov[iov_cnt].iov_base = &spxy;
> > +           iov[iov_cnt].iov_len = ROUNDUP(spxy.ss_len);
> > +           smsg.sadb_msg_len += sa_pxy.sadb_address_len;
> > +           iov_cnt++;
> > +   }
> > +
> >     if (sa_ltime_soft.sadb_lifetime_len) {
> >             /* soft lifetime */
> >             iov[iov_cnt].iov_base = &sa_ltime_soft;
> > @@ -1342,6 +1379,24 @@ pfkey_sa_add(int fd, struct iked_childsa *sa, struct 
> > iked_childsa *last)
> >     return (0);
> >  }
> >  
> > +int
> > +pfkey_sa_update_addresses(int fd, struct iked_childsa *sa)
> > +{
> > +   uint8_t          satype;
> > +
> > +   if (!sa->csa_ikesa)
> > +           return (-1);
> > +   /* check if peer has changed */
> > +   if (sa->csa_ikesa->sa_peer_loaded.addr.ss_family == AF_UNSPEC ||
> > +       memcmp(&sa->csa_ikesa->sa_peer_loaded, &sa->csa_ikesa->sa_peer,
> > +       sizeof(sa->csa_ikesa->sa_peer_loaded)) == 0)
> > +           return (0);
> > +   if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
> > +           return (-1);
> > +   log_debug("%s: spi %s", __func__, print_spi(sa->csa_spi.spi, 4));
> > +   return pfkey_sa(fd, satype, IKED_SADB_UPDATE_SA_ADDRESSES, sa);
> > +}
> > +
> >  int
> >  pfkey_sa_delete(int fd, struct iked_childsa *sa)
> >  {
> > diff --git a/sbin/iked/policy.c b/sbin/iked/policy.c
> > index 0719323cc6f..52e5572c15b 100644
> > --- a/sbin/iked/policy.c
> > +++ b/sbin/iked/policy.c
> > @@ -404,6 +404,45 @@ sa_free(struct iked *env, struct iked_sa *sa)
> >     config_free_sa(env, sa);
> >  }
> >  
> > +/* oflow did replace active flow, so we need to re-activate a matching 
> > flow */
> > +int
> > +flow_replace(struct iked *env, struct iked_flow *oflow)
> > +{
> > +   struct iked_sa          *sa;
> > +   struct iked_flow        *flow, *other;
> > +
> > +   if (!oflow->flow_loaded)
> > +           return (-1);
> > +   RB_FOREACH(sa, iked_sas, &env->sc_sas) {
> > +           if (oflow->flow_ikesa == sa ||
> > +               sa->sa_state != IKEV2_STATE_ESTABLISHED)
> > +                   continue;
> > +           TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
> > +                   if (flow == oflow ||
> > +                       flow->flow_loaded || !flow_equal(flow, oflow))
> > +                           continue;
> > +                   if ((other = RB_FIND(iked_flows, &env->sc_activeflows,
> > +                       flow)) != NULL) {
> > +                           /* XXX should not happen */
> > +                           log_debug("%s: found flow %p for %p/%p",
> > +                               __func__, other, flow, other);
> > +                           return (-1);
> > +                   }
> > +                   if (pfkey_flow_add(env->sc_pfkey, flow) != 0) {
> > +                           log_debug("%s: failed to load flow", __func__);
> > +                           return (-1);
> > +                   }
> > +                   RB_INSERT(iked_flows, &env->sc_activeflows, flow);
> > +                   log_debug("%s: loaded flow %p replaces %p", __func__,
> > +                       flow, oflow);
> > +                   /* check for matching flow if we get deleted, too */
> > +                   flow->flow_replacing = 1;
> > +                   return (0);
> > +           }
> > +   }
> > +   return (-1);
> > +}
> > +
> >  void
> >  sa_free_flows(struct iked *env, struct iked_saflows *head)
> >  {
> > @@ -417,7 +456,9 @@ sa_free_flows(struct iked *env, struct iked_saflows 
> > *head)
> >             if (flow->flow_loaded)
> >                     RB_REMOVE(iked_flows, &env->sc_activeflows, flow);
> >             TAILQ_REMOVE(head, flow, flow_entry);
> > -           (void)pfkey_flow_delete(env->sc_pfkey, flow);
> > +           if (!flow->flow_replacing ||
> > +               flow_replace(env, flow) != 0)
> > +                   (void)pfkey_flow_delete(env->sc_pfkey, flow);
> >             flow_free(flow);
> >     }
> >  }
> > diff --git a/sbin/iked/types.h b/sbin/iked/types.h
> > index df358ee4e7d..2f32a8f2ffb 100644
> > --- a/sbin/iked/types.h
> > +++ b/sbin/iked/types.h
> > @@ -59,6 +59,9 @@
> >  #define IKED_COOKIE_MIN            1       /* min 1 bytes */
> >  #define IKED_COOKIE_MAX            64      /* max 64 bytes */
> >  
> > +#define IKED_COOKIE2_MIN   8       /* min 8 bytes */
> > +#define IKED_COOKIE2_MAX   64      /* max 64 bytes */
> > +
> >  #define IKED_ID_SIZE               1024    /* XXX should be dynamic */
> >  #define IKED_PSK_SIZE              1024    /* XXX should be dynamic */
> >  #define IKED_MSGBUF_MAX            8192
> > @@ -99,6 +102,7 @@ enum imsg_type {
> >     IMSG_CTL_DECOUPLE,
> >     IMSG_CTL_ACTIVE,
> >     IMSG_CTL_PASSIVE,
> > +   IMSG_CTL_MOBIKE,
> >     IMSG_COMPILE,
> >     IMSG_UDP_SOCKET,
> >     IMSG_PFKEY_SOCKET,

Reply via email to