Hi Stuart,

I'm glad to see people are using this.
There's some smaller fixes that I haven't sent to the list yet, so
probably I'll send an updated diff on monday.

Regards,
Tobias

On 3/30/19 6:43 PM, Stuart Henderson wrote:
> This diff hasn't gone anywhere recently - I've been using it since
> Tobias posted it with no problems. Any comments on whether it should
> go in, and if so, before/after 6.5? The feature is disabled by default.
> 
> Index: config.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/config.c,v
> retrieving revision 1.49
> diff -u -p -r1.49 config.c
> --- config.c  27 Nov 2017 18:39:35 -0000      1.49
> +++ config.c  30 Mar 2019 17:41:33 -0000
> @@ -94,12 +94,30 @@ config_free_kex(struct iked_kex *kex)
>  }
>  
>  void
> +config_free_fragments(struct iked_frag *frag)
> +{
> +     size_t i;
> +     if (frag && frag->frag_arr) {
> +             for (i = 0; i < frag->frag_count; i++) {
> +                     free(frag->frag_arr[i]->frag_data);
> +                     frag->frag_arr[i]->frag_data = NULL;
> +                     free(frag->frag_arr[i]);
> +                     frag->frag_arr[i] = NULL;
> +             }
> +             free(frag->frag_arr);
> +             frag->frag_arr = NULL;
> +             bzero(frag, sizeof(struct iked_frag));
> +     }
> +}
> +
> +void
>  config_free_sa(struct iked *env, struct iked_sa *sa)
>  {
>       timer_del(env, &sa->sa_timer);
>       timer_del(env, &sa->sa_keepalive);
>       timer_del(env, &sa->sa_rekey);
>  
> +     config_free_fragments(&sa->sa_fragments);
>       config_free_proposals(&sa->sa_proposals, 0);
>       config_free_childsas(env, &sa->sa_childsas, NULL, NULL);
>       sa_free_flows(env, &sa->sa_flows);
> @@ -925,6 +943,29 @@ config_setkeys(struct iked *env)
>       EVP_PKEY_free(key);
>  
>       return (ret);
> +}
> +
> +int
> +config_setfragmentation(struct iked *env)
> +{
> +     unsigned int boolval;
> +
> +     boolval = env->sc_frag;
> +     proc_compose(&env->sc_ps, PROC_IKEV2, IMSG_CTL_FRAGMENTATION,
> +         &boolval, sizeof(boolval));
> +     return (0);
> +}
> +
> +int
> +config_getfragmentation(struct iked *env, struct imsg *imsg)
> +{
> +     unsigned int boolval;
> +
> +     IMSG_SIZE_CHECK(imsg, &boolval);
> +     memcpy(&boolval, imsg->data, sizeof(boolval));
> +     env->sc_frag = boolval;
> +     log_debug("%s: %sfragmentation", __func__, env->sc_frag ? "" : "no ");
> +     return (0);
>  }
>  
>  int
> Index: iked.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.c,v
> retrieving revision 1.36
> diff -u -p -r1.36 iked.c
> --- iked.c    27 Nov 2017 18:39:35 -0000      1.36
> +++ iked.c    30 Mar 2019 17:41:33 -0000
> @@ -251,6 +251,7 @@ parent_configure(struct iked *env)
>               fatal("pledge");
>  
>       config_setmobike(env);
> +     config_setfragmentation(env);
>       config_setcoupled(env, env->sc_decoupled ? 0 : 1);
>       config_setmode(env, env->sc_passive ? 1 : 0);
>       config_setocsp(env);
> @@ -282,6 +283,7 @@ parent_reload(struct iked *env, int rese
>               config_setcompile(env, PROC_IKEV2);
>  
>               config_setmobike(env);
> +             config_setfragmentation(env);
>               config_setcoupled(env, env->sc_decoupled ? 0 : 1);
>               config_setmode(env, env->sc_passive ? 1 : 0);
>               config_setocsp(env);
> Index: iked.conf.5
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.conf.5,v
> retrieving revision 1.53
> diff -u -p -r1.53 iked.conf.5
> --- iked.conf.5       31 Jan 2018 13:25:55 -0000      1.53
> +++ iked.conf.5       30 Mar 2019 17:41:33 -0000
> @@ -136,6 +136,12 @@ 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 fragmentation
> +Enable IKEv2 Message Fragmentation (RFC 7383) support.
> +This allows IKEv2 to operate in environments that might block IP fragments.
> +.It Ic set nofragmentation
> +Disables IKEv2 Message Fragmentation support.
> +This is the default.
>  .It Ic set mobike
>  Enable MOBIKE (RFC 4555) support.
>  This is the default.
> Index: iked.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.h,v
> retrieving revision 1.119
> diff -u -p -r1.119 iked.h
> --- iked.h    6 Aug 2018 06:30:06 -0000       1.119
> +++ iked.h    30 Mar 2019 17:41:33 -0000
> @@ -362,6 +362,21 @@ struct iked_kex {
>       struct ibuf                     *kex_dhpeer;    /* pointer to i or r */
>  };
>  
> +struct iked_frag_entry {
> +     uint8_t *frag_data;
> +     size_t   frag_size;
> +};
> +
> +struct iked_frag {
> +     struct iked_frag_entry          **frag_arr;     /* list of fragment 
> buffers */
> +     size_t                            frag_count;   /* number of fragments 
> received */
> +#define IKED_FRAG_TOTAL_MAX  111     /* upper limit of frag_total (64kB / 
> 576B) */
> +     size_t                            frag_total;   /* total numbe of 
> fragments */
> +     size_t                            frag_total_size;
> +     uint8_t                           frag_nextpayload;
> +
> +};
> +
>  struct iked_sa {
>       struct iked_sahdr                sa_hdr;
>       uint32_t                         sa_msgid;      /* Last request rcvd */
> @@ -377,6 +392,8 @@ struct iked_sa {
>       struct iked_addr                 sa_local;
>       int                              sa_fd;
>  
> +     struct iked_frag                 sa_fragments;
> +
>       int                              sa_natt;       /* for IKE messages */
>       int                              sa_udpencap;   /* for pfkey */
>       int                              sa_usekeepalive;/* NAT-T keepalive */
> @@ -445,6 +462,7 @@ struct iked_sa {
>       uint16_t                         sa_cpi_in;     /* IPcomp incoming*/
>  
>       int                              sa_mobike;     /* MOBIKE */
> +     int                              sa_frag;       /* fragmentation */
>  
>       struct iked_timer                sa_timer;      /* SA timeouts */
>  #define IKED_IKE_SA_EXCHANGE_TIMEOUT  300            /* 5 minutes */
> @@ -602,6 +620,7 @@ struct iked {
>       uint8_t                          sc_decoupled;
>  
>       uint8_t                          sc_mobike;     /* MOBIKE */
> +     uint8_t                          sc_frag;       /* fragmentation */
>  
>       struct iked_policies             sc_policies;
>       struct iked_policy              *sc_defaultcon;
> @@ -653,6 +672,7 @@ int        control_listen(struct control_sock 
>  struct iked_policy *
>        config_new_policy(struct iked *);
>  void  config_free_kex(struct iked_kex *);
> +void  config_free_fragments(struct iked_frag *frag);
>  void  config_free_sa(struct iked *, struct iked_sa *);
>  struct iked_sa *
>        config_new_sa(struct iked *, int);
> @@ -701,6 +721,8 @@ int        config_setkeys(struct iked *);
>  int   config_getkey(struct iked *, struct imsg *);
>  int   config_setmobike(struct iked *);
>  int   config_getmobike(struct iked *, struct imsg *);
> +int   config_setfragmentation(struct iked *);
> +int   config_getfragmentation(struct iked *, struct imsg *);
>  
>  /* policy.c */
>  void  policy_init(struct iked *);
> @@ -859,6 +881,12 @@ void      ikev2_msg_flushqueue(struct iked *
>  struct iked_message *
>        ikev2_msg_lookup(struct iked *, struct iked_msgqueue *,
>           struct iked_message *, struct ike_header *);
> +void  ikev2_msg_lookup_dispose_all(struct iked *env,
> +         struct iked_msgqueue *queue, struct iked_message *msg,
> +         struct ike_header *hdr);
> +int   ikev2_msg_lookup_retransmit_all(struct iked *env,
> +         struct iked_msgqueue *queue, struct iked_message *msg,
> +         struct ike_header *hdr, struct iked_sa *sa);
>  
>  /* ikev2_pld.c */
>  int   ikev2_pld_parse(struct iked *, struct ike_header *,
> Index: ikev2.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2.c,v
> retrieving revision 1.168
> diff -u -p -r1.168 ikev2.c
> --- ikev2.c   27 Feb 2019 06:33:56 -0000      1.168
> +++ ikev2.c   30 Mar 2019 17:41:33 -0000
> @@ -144,6 +144,8 @@ ssize_t   ikev2_add_sighashnotify(struct i
>           ssize_t);
>  ssize_t ikev2_add_nat_detection(struct iked *, struct ibuf *,
>           struct ikev2_payload **, struct iked_message *, ssize_t);
> +ssize_t ikev2_add_fragmentation(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 *);
> @@ -202,6 +204,8 @@ ikev2_dispatch_parent(int fd, struct pri
>               return (0);
>       case IMSG_CTL_MOBIKE:
>               return (config_getmobike(env, imsg));
> +     case IMSG_CTL_FRAGMENTATION:
> +             return (config_getfragmentation(env, imsg));
>       case IMSG_UDP_SOCKET:
>               return (config_getsocket(env, imsg, ikev2_msg_cb));
>       case IMSG_PFKEY_SOCKET:
> @@ -399,9 +403,9 @@ void
>  ikev2_recv(struct iked *env, struct iked_message *msg)
>  {
>       struct ike_header       *hdr;
> -     struct iked_message     *m;
>       struct iked_sa          *sa;
>       unsigned int             initiator, flag = 0;
> +     int                      r;
>  
>       hdr = ibuf_seek(msg->msg_data, msg->msg_offset, sizeof(*hdr));
>  
> @@ -442,7 +446,8 @@ ikev2_recv(struct iked *env, struct iked
>               if (msg->msg_msgid > sa->sa_reqid)
>                       return;
>               if (hdr->ike_exchange != IKEV2_EXCHANGE_INFORMATIONAL &&
> -                 !ikev2_msg_lookup(env, &sa->sa_requests, msg, hdr))
> +                 !ikev2_msg_lookup(env, &sa->sa_requests, msg, hdr) &&
> +                 sa->sa_fragments.frag_count == 0)
>                       return;
>               if (flag) {
>                       if ((sa->sa_stateflags & flag) == 0)
> @@ -454,10 +459,9 @@ ikev2_recv(struct iked *env, struct iked
>                       initiator = 1;
>               }
>               /*
> -              * There's no need to keep the request around anymore
> +              * There's no need to keep the request (fragments) around 
> anymore
>                */
> -             if ((m = ikev2_msg_lookup(env, &sa->sa_requests, msg, hdr)))
> -                     ikev2_msg_dispose(env, &sa->sa_requests, m);
> +             ikev2_msg_lookup_dispose_all(env, &sa->sa_requests, msg, hdr);
>       } else {
>               /*
>                * IKE_SA_INIT is special since it always uses the message id 0.
> @@ -483,14 +487,16 @@ ikev2_recv(struct iked *env, struct iked
>               /*
>                * See if we have responded to this request before
>                */
> -             if ((m = ikev2_msg_lookup(env, &sa->sa_responses, msg, hdr))) {
> -                     if (ikev2_msg_retransmit_response(env, sa, m)) {
> +             if ((r = ikev2_msg_lookup_retransmit_all(env, &sa->sa_responses,
> +                 msg, hdr, sa)) != 0) {
> +                     if (r == -1) {
>                               log_warn("%s: failed to retransmit a "
>                                   "response", __func__);
>                               sa_free(env, sa);
>                       }
>                       return;
> -             } else if (sa->sa_msgid_set && msg->msg_msgid == sa->sa_msgid) {
> +             } else if (sa->sa_msgid_set && msg->msg_msgid == sa->sa_msgid &&
> +                       !(sa->sa_fragments.frag_count)) {
>                       /*
>                        * Response is being worked on, most likely we're
>                        * waiting for the CA process to get back to us
> @@ -803,6 +809,9 @@ ikev2_init_recv(struct iked *env, struct
>               return;
>       }
>  
> +     if (sa->sa_fragments.frag_count != 0)
> +             return;
> +
>       if (!ikev2_msg_frompeer(msg))
>               return;
>  
> @@ -1022,6 +1031,13 @@ ikev2_init_ike_sa_peer(struct iked *env,
>               goto done;
>       len = ibuf_size(sa->sa_inonce);
>  
> +     /* Fragmentation Notify */
> +     if (env->sc_frag) {
> +             if ((len = ikev2_add_fragmentation(env, buf, &pld, &req, len))
> +                 == -1)
> +                     goto done;
> +     }
> +
>       if ((env->sc_opts & IKED_OPT_NONATT) == 0) {
>               if (ntohs(port) == IKED_NATT_PORT) {
>                       /* Enforce NAT-T on the initiator side */
> @@ -1973,6 +1989,29 @@ ikev2_add_cp(struct iked *env, struct ik
>  }
>  
>  ssize_t
> +ikev2_add_fragmentation(struct iked *env, struct ibuf *buf,
> +    struct ikev2_payload **pld, struct iked_message *msg, ssize_t len)
> +{
> +     struct ikev2_notify             *n;
> +     uint8_t                         *ptr;
> +
> +     if (*pld != NULL)
> +             if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1)
> +                     return (-1);
> +     if ((*pld = ikev2_add_payload(buf)) == NULL)
> +             return (-1);
> +     len = sizeof(*n);
> +     if ((ptr = ibuf_advance(buf, len)) == NULL)
> +             return (-1);
> +     n = (struct ikev2_notify *) ptr;
> +     n->n_protoid = 0;
> +     n->n_spisize = 0;
> +     n->n_type = htobe16(IKEV2_N_FRAGMENTATION_SUPPORTED);
> +
> +     return (len);
> +}
> +
> +ssize_t
>  ikev2_add_proposals(struct iked *env, struct iked_sa *sa, struct ibuf *buf,
>      struct iked_proposals *proposals, uint8_t protoid, int initiator,
>      int sendikespi, int skipdh)
> @@ -2283,6 +2322,9 @@ ikev2_resp_recv(struct iked *env, struct
>       if ((sa = msg->msg_sa) == NULL)
>               return;
>  
> +     if (sa->sa_fragments.frag_count !=0)
> +             return;
> +
>       if (msg->msg_natt && sa->sa_natt == 0) {
>               log_debug("%s: NAT-T message received, updated SA", __func__);
>               sa->sa_natt = 1;
> @@ -2407,6 +2449,13 @@ ikev2_resp_ike_sa_init(struct iked *env,
>               goto done;
>       len = ibuf_size(sa->sa_rnonce);
>  
> +     /* Fragmentation Notify*/
> +     if (sa->sa_frag) {
> +             if ((len = ikev2_add_fragmentation(env, buf, &pld, &resp, len))
> +                 == -1)
> +                     goto done;
> +     }
> +
>       if ((env->sc_opts & IKED_OPT_NONATT) == 0 &&
>           msg->msg_local.ss_family != AF_UNSPEC) {
>               if ((len = ikev2_add_nat_detection(env, buf, &pld, &resp, len))
> @@ -2485,6 +2534,7 @@ ikev2_send_auth_failed(struct iked *env,
>       timer_del(env, &sa->sa_timer);
>       timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa);
>       timer_add(env, &sa->sa_timer, IKED_IKE_SA_DELETE_TIMEOUT);
> +     config_free_fragments(&sa->sa_fragments);
>  
>       return (ret);
>  }
> Index: ikev2.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2.h,v
> retrieving revision 1.28
> diff -u -p -r1.28 ikev2.h
> --- ikev2.h   27 Feb 2019 06:33:57 -0000      1.28
> +++ ikev2.h   30 Mar 2019 17:41:33 -0000
> @@ -78,6 +78,11 @@ struct ikev2_payload {
>       uint16_t         pld_length;            /* Payload length with header */
>  } __packed;
>  
> +struct ikev2_frag_payload {
> +     uint16_t         frag_num;              /* current fragment message 
> number */
> +     uint16_t         frag_total;            /* total number of fragment 
> messages */
> +} __packed;
> +
>  #define IKEV2_CRITICAL_PAYLOAD       0x01    /* First bit in the reserved 
> field */
>  
>  /* IKEv2 payload types */
> @@ -99,6 +104,7 @@ struct ikev2_payload {
>  #define IKEV2_PAYLOAD_CP     47      /* Configuration Payload */
>  #define IKEV2_PAYLOAD_EAP    48      /* Extensible Authentication */
>  #define IKEV2_PAYLOAD_GSPM   49      /* RFC6467 Generic Secure Password */
> +#define IKEV2_PAYLOAD_SKF    53      /* RFC7383 Encrypted Fragment Payload */
>  
>  extern struct iked_constmap ikev2_payload_map[];
>  
> @@ -243,6 +249,11 @@ extern struct iked_constmap ikev2_xforma
>  #define IKEV2_XFORMDH_X_CURVE25519   1034    /* 
> draft-ietf-ipsecme-safecurves-00 */
>  
>  extern struct iked_constmap ikev2_xformdh_map[];
> +
> +#define IKEV2_IPV4_OVERHEAD          (20 + 8 + 28) /* IPv4 + UDP + IKE_HDR*/
> +#define IKEV2_MAXLEN_IPV4_FRAG               (576 - IKEV2_IPV4_OVERHEAD)
> +#define IKEV2_IPV6_OVERHEAD          (40 + 8 + 28) /* IPv6 + UDP + IKE_HDR*/
> +#define IKEV2_MAXLEN_IPV6_FRAG               (1280 - IKEV2_IPV6_OVERHEAD)
>  
>  #define IKEV2_XFORMESN_NONE          0       /* No ESN */
>  #define IKEV2_XFORMESN_ESN           1       /* ESN */
> Index: ikev2_msg.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
> retrieving revision 1.53
> diff -u -p -r1.53 ikev2_msg.c
> --- ikev2_msg.c       27 Nov 2017 18:39:35 -0000      1.53
> +++ ikev2_msg.c       30 Mar 2019 17:41:33 -0000
> @@ -46,6 +46,9 @@
>  void  ikev1_recv(struct iked *, struct iked_message *);
>  void  ikev2_msg_response_timeout(struct iked *, void *);
>  void  ikev2_msg_retransmit_timeout(struct iked *, void *);
> +int   ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf);
> +int   ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
> +         struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int 
> response);
>  
>  void
>  ikev2_msg_cb(int fd, short event, void *arg)
> @@ -616,7 +619,8 @@ ikev2_msg_decrypt(struct iked *env, stru
>           __func__, outlen, encrlen, pad);
>       print_hex(ibuf_data(out), 0, ibuf_size(out));
>  
> -     if (ibuf_setsize(out, outlen) != 0)
> +     /* Strip padding and padding length */
> +     if (ibuf_setsize(out, outlen - pad - 1) != 0)
>               goto done;
>  
>       ibuf_release(src);
> @@ -629,6 +633,25 @@ ikev2_msg_decrypt(struct iked *env, stru
>  }
>  
>  int
> +ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf) {
> +     size_t          len = ibuf_length(buf);
> +     sa_family_t     sa_fam;
> +     size_t          max;
> +     size_t          ivlen, integrlen, blocklen;
> +
> +     sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
> +
> +     max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
> +                             : IKEV2_MAXLEN_IPV6_FRAG;
> +
> +     blocklen = cipher_length(sa->sa_encr);
> +     ivlen = cipher_ivlength(sa->sa_encr);
> +     integrlen = hash_length(sa->sa_integr);
> +
> +     return ((len + ivlen + blocklen + integrlen) >= max) && sa->sa_frag;
> +}
> +
> +int
>  ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf 
> **ep,
>      uint8_t exchange, uint8_t firstpayload, int response)
>  {
> @@ -638,6 +661,12 @@ ikev2_msg_send_encrypt(struct iked *env,
>       struct ibuf                     *buf, *e = *ep;
>       int                              ret = -1;
>  
> +     /* Check if msg needs to be fragmented */
> +     if (ikev2_check_frag_oversize(sa, e)) {
> +             return ikev2_send_encrypted_fragments(env, sa, e, exchange,
> +                 firstpayload, response);
> +     }
> +
>       if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
>           sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
>           sa->sa_local.addr.ss_len, response)) == NULL)
> @@ -689,6 +718,123 @@ ikev2_msg_send_encrypt(struct iked *env,
>       return (ret);
>  }
>  
> +int
> +ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
> +    struct ibuf *in, uint8_t exchange, uint8_t firstpayload, int response) {
> +     struct iked_message              resp;
> +     struct ibuf                     *buf, *e;
> +     struct ike_header               *hdr;
> +     struct ikev2_payload            *pld;
> +     struct ikev2_frag_payload       *frag;
> +     sa_family_t                      sa_fam;
> +     size_t                           ivlen, integrlen, blocklen;
> +     size_t                           max_len, left,  offset=0;;
> +     size_t                           frag_num = 1, frag_total;
> +     uint8_t                         *data;
> +     uint32_t                         msgid;
> +     int                              ret = -1;
> +
> +     sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
> +
> +     left = ibuf_length(in);
> +
> +     /* Calculate max allowed size of a fragments payload */
> +     blocklen = cipher_length(sa->sa_encr);
> +     ivlen = cipher_ivlength(sa->sa_encr);
> +     integrlen = hash_length(sa->sa_integr);
> +     max_len = (sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
> +                                  : IKEV2_MAXLEN_IPV6_FRAG)
> +                  - ivlen - blocklen - integrlen;
> +
> +     /* Total number of fragments to send */
> +     frag_total = (left / max_len) + 1;
> +
> +     msgid = response ? sa->sa_msgid : ikev2_msg_id(env, sa);
> +
> +     while (frag_num <= frag_total) {
> +             if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
> +                 sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
> +                 sa->sa_local.addr.ss_len, response)) == NULL)
> +                     goto done;
> +
> +             resp.msg_msgid = msgid;
> +
> +             /* IKE header */
> +             if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid,
> +                 IKEV2_PAYLOAD_SKF, exchange, response ? IKEV2_FLAG_RESPONSE
> +                                                       : 0)) == NULL)
> +                     goto done;
> +
> +             /* Payload header */
> +             if ((pld = ikev2_add_payload(buf)) == NULL)
> +                     goto done;
> +
> +             /* Fragment header */
> +             if ((frag = ibuf_advance(buf, sizeof(*frag))) == NULL) {
> +                     log_debug("%s: failed to add SKF fragment header",
> +                         __func__);
> +                     goto done;
> +             }
> +             frag->frag_num = htobe16(frag_num);
> +             frag->frag_total = htobe16(frag_total);
> +
> +             /* Encrypt message and add as an E payload */
> +             data = ibuf_seek(in, offset, 0);
> +             if((e=ibuf_new(data, MIN(left, max_len))) == NULL) {
> +                     goto done;
> +             }
> +             if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> +                     log_debug("%s: encryption failed", __func__);
> +                     goto done;
> +             }
> +             if (ibuf_cat(buf, e) != 0)
> +                     goto done;
> +
> +             if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag),
> +                 firstpayload) == -1)
> +                     goto done;
> +
> +             if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
> +                     goto done;
> +
> +             /* Add integrity checksum (HMAC) */
> +             if (ikev2_msg_integr(env, sa, buf) != 0) {
> +                     log_debug("%s: integrity checksum failed", __func__);
> +                     goto done;
> +             }
> +
> +             log_debug("%s: Fragment %zu of %zu has size of %zu bytes.",
> +                 __func__, frag_num, frag_total,
> +                 ibuf_size(buf) - sizeof(*hdr));
> +             print_hex(ibuf_data(buf), 0,  ibuf_size(buf));
> +
> +             resp.msg_data = buf;
> +             resp.msg_sa = sa;
> +             resp.msg_fd = sa->sa_fd;
> +             TAILQ_INIT(&resp.msg_proposals);
> +
> +             if (ikev2_msg_send(env, &resp) == -1)
> +                     goto done;
> +
> +             offset += MIN(left, max_len);
> +             left -= MIN(left, max_len);
> +             frag_num++;
> +
> +             /* MUST be zero after first fragment */
> +             firstpayload = 0;
> +
> +             ikev2_msg_cleanup(env, &resp);
> +             ibuf_release(e);
> +             e = NULL;
> +     }
> +
> +     return 0;
> +done:
> +     ikev2_msg_cleanup(env, &resp);
> +     ibuf_release(e);
> +     return ret;
> +}
> +
>  struct ibuf *
>  ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response)
>  {
> @@ -990,6 +1136,42 @@ ikev2_msg_lookup(struct iked *env, struc
>       }
>  
>       return (m);
> +}
> +
> +void
> +ikev2_msg_lookup_dispose_all(struct iked *env, struct iked_msgqueue *queue,
> +    struct iked_message *msg, struct ike_header *hdr)
> +{
> +     struct iked_message     *m = NULL, *tmp = NULL;
> +
> +     TAILQ_FOREACH_SAFE(m, queue, msg_entry, tmp) {
> +             if (m->msg_msgid == msg->msg_msgid &&
> +                 m->msg_exchange == hdr->ike_exchange) {
> +                     TAILQ_REMOVE(queue, m, msg_entry);
> +                     timer_del(env, &m->msg_timer);
> +                     ikev2_msg_cleanup(env, m);
> +                     free(m);
> +             }
> +     }
> +}
> +
> +int
> +ikev2_msg_lookup_retransmit_all(struct iked *env, struct iked_msgqueue 
> *queue,
> +    struct iked_message *msg, struct ike_header *hdr, struct iked_sa *sa)
> +{
> +     struct iked_message     *m = NULL, *tmp = NULL;
> +     int count = 0;
> +
> +     TAILQ_FOREACH_SAFE(m, queue, msg_entry, tmp) {
> +             if (m->msg_msgid == msg->msg_msgid &&
> +                 m->msg_exchange == hdr->ike_exchange) {
> +                     if (ikev2_msg_retransmit_response(env, sa, msg)) {
> +                             return -1;
> +                     }
> +                     count++;
> +             }
> +     }
> +     return count;
>  }
>  
>  int
> Index: ikev2_pld.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2_pld.c,v
> retrieving revision 1.70
> diff -u -p -r1.70 ikev2_pld.c
> --- ikev2_pld.c       22 Mar 2018 21:11:49 -0000      1.70
> +++ ikev2_pld.c       30 Mar 2019 17:41:33 -0000
> @@ -95,6 +95,10 @@ int         ikev2_pld_auth(struct iked *, struc
>           struct iked_message *, size_t, size_t);
>  int   ikev2_pld_e(struct iked *, struct ikev2_payload *,
>           struct iked_message *, size_t, size_t);
> +int   ikev2_pld_ef(struct iked *env, struct ikev2_payload *pld,
> +         struct iked_message *msg, size_t offset, size_t left);
> +int   ikev2_frags_reassemble(struct iked *env,
> +         struct ikev2_payload *pld, struct iked_message *msg);
>  int   ikev2_validate_cp(struct iked_message *, size_t, size_t,
>           struct ikev2_cp *);
>  int   ikev2_pld_cp(struct iked *, struct ikev2_payload *,
> @@ -249,6 +253,9 @@ ikev2_pld_payloads(struct iked *env, str
>               case IKEV2_PAYLOAD_SK:
>                       ret = ikev2_pld_e(env, &pld, msg, offset, left);
>                       break;
> +             case IKEV2_PAYLOAD_SKF:
> +                     ret = ikev2_pld_ef(env, &pld, msg, offset, left);
> +                     break;
>               case IKEV2_PAYLOAD_CP | IKED_E:
>                       ret = ikev2_pld_cp(env, &pld, msg, offset, left);
>                       break;
> @@ -266,8 +273,8 @@ ikev2_pld_payloads(struct iked *env, str
>                       return (-1);
>               }
>  
> -             /* Encrypted payload must appear last */
> -             if (payload == IKEV2_PAYLOAD_SK)
> +             /* Encrypted payloads must appear last */
> +             if ((payload == IKEV2_PAYLOAD_SK) || (payload == 
> IKEV2_PAYLOAD_SKF))
>                       return (0);
>  
>               payload = pld.pld_nextpayload;
> @@ -1251,6 +1258,23 @@ ikev2_pld_notify(struct iked *env, struc
>               }
>               msg->msg_parent->msg_cookie = msg->msg_cookie;
>               break;
> +     case IKEV2_N_FRAGMENTATION_SUPPORTED:
> +             if (msg->msg_e) {
> +                     log_debug("%s: N_FRAGMENTATION_SUPPORTED encrypted",
> +                         __func__);
> +                     return (-1);
> +             }
> +             if (len != 0) {
> +                     log_debug("%s: ignoring malformed fragmentation"
> +                         " notification: %zu", __func__, len);
> +                     return (0);
> +             }
> +             if (!env->sc_frag) {
> +                     log_debug("%s: fragmentation disabled", __func__);
> +                     return (0);
> +             }
> +             msg->msg_sa->sa_frag = 1;
> +             break;
>       case IKEV2_N_SIGNATURE_HASH_ALGORITHMS:
>               if (msg->msg_e) {
>                       log_debug("%s: SIGNATURE_HASH_ALGORITHMS: encrypted",
> @@ -1585,6 +1609,164 @@ ikev2_pld_ts(struct iked *env, struct ik
>  }
>  
>  int
> +ikev2_pld_ef(struct iked *env, struct ikev2_payload *pld,
> +    struct iked_message *msg, size_t offset, size_t left)
> +{
> +     struct iked_sa                  *sa = msg->msg_sa;
> +     struct iked_frag                *sa_frag = &sa->sa_fragments;
> +     struct iked_frag_entry          *el = NULL;
> +     struct ikev2_frag_payload        frag;
> +     uint8_t                         *msgbuf = ibuf_data(msg->msg_data);
> +     uint8_t                         *buf;
> +     struct ibuf                     *e = NULL;
> +     size_t                           frag_num, frag_total;
> +     size_t                           len;
> +     int                              ret = -1;
> +     ssize_t                          elen;
> +
> +     buf = msgbuf + offset;
> +     memcpy(&frag, buf, sizeof(frag));
> +     frag_num = betoh16(frag.frag_num);
> +     frag_total = betoh16(frag.frag_total);
> +
> +     offset += sizeof(frag);
> +     buf = msgbuf + offset;
> +     len = left - sizeof(frag);
> +
> +     /* Limit number of total fragments to avoid DOS */
> +     if (frag_total > IKED_FRAG_TOTAL_MAX ) {
> +             log_debug("%s: Total Fragments too big  %zu",
> +                 __func__, frag_total);
> +             goto dropall;
> +     }
> +
> +     /* Check sanity of fragment header */
> +     if (frag_num == 0 || frag_total == 0) {
> +             log_debug("%s: Malformed fragment received: %zu of %zu",
> +                 __func__, frag_num, frag_total);
> +             goto done;
> +     }
> +     log_debug("%s: Received fragment: %zu of %zu",
> +          __func__, frag_num, frag_total);
> +
> +     /* Check new fragmented message */
> +     if (sa_frag->frag_arr == NULL) {
> +             sa_frag->frag_arr = reallocarray(NULL, frag_total, 
> sizeof(uint8_t*));
> +             bzero(sa_frag->frag_arr, frag_total * sizeof(uint8_t*));
> +             sa_frag->frag_total = frag_total;
> +             sa_frag->frag_nextpayload = pld->pld_nextpayload;
> +     }
> +
> +     /* Drop all fragments if frag_num or frag_total don't match */
> +     if (frag_num > sa_frag->frag_total || frag_total > sa_frag->frag_total)
> +             goto dropall;
> +
> +     /* Silent drop if fragment already stored */
> +     if (sa_frag->frag_arr[frag_num-1] != NULL)
> +             goto done;
> +
> +        /* Decrypt fragment */
> +     if ((e = ibuf_new(buf, len)) == NULL)
> +             goto done;
> +
> +     if ((e = ikev2_msg_decrypt(env, msg->msg_sa, msg->msg_data, e))
> +         == NULL ) {
> +             log_debug("%s: Failed to decrypt fragment: %zu of %zu",
> +                 __func__, frag_num, frag_total);
> +             goto done;
> +     }
> +     elen = ibuf_length(e);
> +
> +     /* Insert new list element */
> +     el = malloc(sizeof(struct iked_frag_entry));
> +     if (el == NULL) {
> +             log_debug("%s: Failed allocating new fragment: %zu of %zu",
> +                 __func__, frag_num, frag_total);
> +             goto done;
> +     }
> +     bzero(el, sizeof(*el));
> +
> +     sa_frag->frag_arr[frag_num-1] = el;
> +     el->frag_size = elen;
> +     el->frag_data = malloc(elen);
> +     if (el->frag_data == NULL) {
> +             log_debug("%s: Failed allocating new fragment data: %zu of %zu",
> +                 __func__, frag_num, frag_total);
> +             goto done;
> +     }
> +     bzero(el->frag_data, sizeof(elen));
> +
> +     /* Copy plaintext to fragment */
> +     memcpy(el->frag_data, ibuf_seek(e, 0, 0), elen);
> +     sa_frag->frag_total_size += elen;
> +     sa_frag->frag_count++;
> +
> +     /* If all frags are received start reassembly */
> +     if (sa_frag->frag_count == sa_frag->frag_total) {
> +             log_debug("%s: All fragments received: %zu of %zu",
> +                 __func__, frag_num, frag_total);
> +             ret = ikev2_frags_reassemble(env, pld, msg);
> +     } else {
> +             ret = 0;
> +     }
> +done:
> +     ibuf_release(e);
> +     return (ret);
> +dropall:
> +     config_free_fragments(sa_frag);
> +     ibuf_release(e);
> +     return -1;
> +}
> +
> +int
> +ikev2_frags_reassemble(struct iked *env, struct ikev2_payload *pld,
> +    struct iked_message *msg)
> +{
> +     struct iked_frag                *sa_frag = &msg->msg_sa->sa_fragments;
> +     struct ibuf                     *e = NULL;
> +     struct iked_frag_entry          *el;
> +     size_t                           offset = 0;
> +     size_t                           i;
> +     struct iked_message              emsg;
> +     int                              ret = -1;
> +
> +     /* Reassemble fragments to single buffer */
> +     if ((e = ibuf_new(NULL, sa_frag->frag_total_size)) == NULL) {
> +             log_debug("%s: Failed allocating SK buffer.", __func__);
> +             goto done;
> +     }
> +
> +     /* Empty queue to new buffer */
> +     for (i = 0; i < sa_frag->frag_total; i++) {
> +             el = sa_frag->frag_arr[i];
> +             memcpy(ibuf_seek(e, offset, 0), el->frag_data, el->frag_size);
> +             offset += el->frag_size;
> +     }
> +
> +     log_debug("%s: Defragmented length %zd", __func__,
> +         sa_frag->frag_total_size);
> +     print_hex(ibuf_data(e), 0,  sa_frag->frag_total_size);
> +
> +     /*
> +      * Parse decrypted payload
> +      */
> +     bzero(&emsg, sizeof(emsg));
> +     memcpy(&emsg, msg, sizeof(*msg));
> +     emsg.msg_data = e;
> +     emsg.msg_e = 1;
> +     emsg.msg_parent = msg;
> +     TAILQ_INIT(&emsg.msg_proposals);
> +
> +     ret = ikev2_pld_payloads(env, &emsg, 0, ibuf_size(e),
> +         sa_frag->frag_nextpayload);
> +done:
> +     config_free_fragments(sa_frag);
> +     ibuf_release(e);
> +
> +     return (ret);
> +}
> +
> +int
>  ikev2_pld_e(struct iked *env, struct ikev2_payload *pld,
>      struct iked_message *msg, size_t offset, size_t left)
>  {
> @@ -1595,6 +1777,13 @@ ikev2_pld_e(struct iked *env, struct ike
>       uint8_t                 *buf;
>       size_t                   len;
>       int                      ret = -1;
> +
> +     if (sa->sa_fragments.frag_arr != NULL) {
> +             log_warn("%s: Received SK payload when SKFs are in queue.",
> +                 __func__);
> +             config_free_fragments(&sa->sa_fragments);
> +             return (ret);
> +     }
>  
>       buf = msgbuf + offset;
>       len = left;
> Index: parse.y
> ===================================================================
> RCS file: /cvs/src/sbin/iked/parse.y,v
> retrieving revision 1.78
> diff -u -p -r1.78 parse.y
> --- parse.y   13 Feb 2019 22:57:07 -0000      1.78
> +++ parse.y   30 Mar 2019 17:41:33 -0000
> @@ -105,6 +105,7 @@ static int                 rules = 0;
>  static int            passive = 0;
>  static int            decouple = 0;
>  static int            mobike = 1;
> +static int            fragmentation = 0;
>  static char          *ocsp_url = NULL;
>  
>  struct ipsec_xf {
> @@ -395,6 +396,7 @@ typedef struct {
>  %token       IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET
>  %token       INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT
>  %token       IPCOMP OCSP IKELIFETIME MOBIKE NOMOBIKE
> +%token       FRAGMENTATION NOFRAGMENTATION
>  %token       <v.string>              STRING
>  %token       <v.number>              NUMBER
>  %type        <v.string>              string
> @@ -455,6 +457,8 @@ set               : SET ACTIVE    { passive = 0; }
>               | SET PASSIVE   { passive = 1; }
>               | SET COUPLE    { decouple = 0; }
>               | SET DECOUPLE  { decouple = 1; }
> +             | SET FRAGMENTATION     { fragmentation = 1; }
> +             | SET NOFRAGMENTATION   { fragmentation = 0; }
>               | SET MOBIKE    { mobike = 1; }
>               | SET NOMOBIKE  { mobike = 0; }
>               | SET OCSP STRING               {
> @@ -1167,6 +1171,7 @@ lookup(char *s)
>               { "esp",                ESP },
>               { "file",               FILENAME },
>               { "flow",               FLOW },
> +             { "fragmentation",      FRAGMENTATION },
>               { "from",               FROM },
>               { "group",              GROUP },
>               { "ike",                IKEV1 },
> @@ -1181,6 +1186,7 @@ lookup(char *s)
>               { "local",              LOCAL },
>               { "mobike",             MOBIKE },
>               { "name",               NAME },
> +             { "nofragmentation",    NOFRAGMENTATION },
>               { "nomobike",           NOMOBIKE },
>               { "ocsp",               OCSP },
>               { "passive",            PASSIVE },
> @@ -1579,6 +1585,7 @@ parse_config(const char *filename, struc
>       free(ocsp_url);
>  
>       mobike = 1;
> +     fragmentation = 0;
>       decouple = passive = 0;
>       ocsp_url = NULL;
>  
> @@ -1592,6 +1599,7 @@ parse_config(const char *filename, struc
>       env->sc_passive = passive ? 1 : 0;
>       env->sc_decoupled = decouple ? 1 : 0;
>       env->sc_mobike = mobike;
> +     env->sc_frag = fragmentation;
>       env->sc_ocsp_url = ocsp_url;
>  
>       if (!rules)
> Index: types.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/types.h,v
> retrieving revision 1.29
> diff -u -p -r1.29 types.h
> --- types.h   27 Nov 2017 18:39:35 -0000      1.29
> +++ types.h   30 Mar 2019 17:41:33 -0000
> @@ -103,6 +103,7 @@ enum imsg_type {
>       IMSG_CTL_ACTIVE,
>       IMSG_CTL_PASSIVE,
>       IMSG_CTL_MOBIKE,
> +     IMSG_CTL_FRAGMENTATION,
>       IMSG_COMPILE,
>       IMSG_UDP_SOCKET,
>       IMSG_PFKEY_SOCKET,
> 

Reply via email to