Tim Stewart <[email protected]> writes:
> On 3/30/19 3:11 PM, Tobias Heider wrote:
>> 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.
>
> I plan to start using this patch this week, likely as soon as you send
> the updated diff. I started on the same feature in June of 2018, but
> other tasks took priority and it stalled.
>
> I have a pretty good testbed for this, as I have several site-to-site
> links that drop UDP fragments and several road warriors that would
> experience fragment drops depending on the cell network they use. I
> will report back on this thread with my findings.
>
> Thanks for the patch!
>
> -TimS
I have been using this patch for over a week and it has increased the
reliability of my road warrior VPN as I move across a variety of
networks. It also allows one of my previously-broken site-to-site VPNs
to function again. Thanks!
I did have one strange problem today, and I have no idea if it's related
to this patch or not. One of my site-to-site connections went down and
I saw the following in one of the peer's log:
Apr 16 12:59:05 site-a iked[15114]: ikev2_msg_send: sendtofrom: No buffer space
available
Apr 16 13:00:07 site-a iked[15114]: ikev2_msg_send: INFORMATIONAL request from
5.6.7.8:500 to 1.2.3.4:500 msgid 929, 80 bytes
Apr 16 13:00:07 site-a iked[15114]: ikev2_recv: INFORMATIONAL response from
responder 1.2.3.4:500 to 5.6.7.8:500 policy 'central' id 929, 80 bytes
Apr 16 13:01:07 site-a iked[15114]: ikev2_msg_send: INFORMATIONAL request from
5.6.7.8:500 to 1.2.3.4:500 msgid 930, 80 bytes
Apr 16 13:01:07 site-a iked[15114]: ikev2_recv: INFORMATIONAL response from
responder 1.2.3.4:500 to 5.6.7.8:500 policy 'central' id 930, 80 bytes
Apr 16 13:02:07 site-a iked[15114]: ikev2_msg_send: INFORMATIONAL request from
5.6.7.8:500 to 1.2.3.4:500 msgid 931, 80 bytes
Apr 16 13:02:07 site-a iked[15114]: ikev2_recv: INFORMATIONAL response from
responder 1.2.3.4:500 to 5.6.7.8:500 policy 'central' id 931, 80 bytes
Apr 16 13:03:00 site-a iked[15114]: ikev2_acquire_sa: flow wasn't found
Apr 16 13:03:07 site-a iked[15114]: pfkey_sa_last_used: message: No such process
Apr 16 13:03:07 site-a iked[15114]: pfkey_sa_last_used: message: No such process
Apr 16 13:03:30 site-a iked[15114]: ikev2_acquire_sa: flow wasn't found
Apr 16 13:04:00 site-a iked[15114]: ikev2_acquire_sa: flow wasn't found
Apr 16 13:04:02 site-a iked[15114]: ikev2_recv: INFORMATIONAL request from
responder 1.2.3.4:500 to 5.6.7.8:500 policy 'central' id 0, 80 bytes
Apr 16 13:04:02 site-a iked[15114]: ikev2_msg_send: INFORMATIONAL response from
5.6.7.8:500 to 1.2.3.4:500 msgid 0, 80 bytes
Apr 16 13:04:07 site-a iked[15114]: pfkey_sa_last_used: message: No such process
Apr 16 13:04:07 site-a iked[15114]: pfkey_sa_last_used: message: No such process
Apr 16 13:04:30 site-a iked[15114]: ikev2_acquire_sa: flow wasn't found
Apr 16 13:05:00 site-a iked[15114]: ikev2_acquire_sa: flow wasn't found
Apr 16 13:05:04 site-a iked[15114]: ikev2_recv: INFORMATIONAL request from
responder 1.2.3.4:500 to 5.6.7.8:500 policy 'central' id 1, 80 bytes
Apr 16 13:05:04 site-a iked[15114]: ikev2_msg_send: INFORMATIONAL response from
5.6.7.8:500 to 1.2.3.4:500 msgid 1, 80 bytes
I restarted site-a's iked and everything came back up and has been
working fine since.
I see there are some other iked patches in-flight on this list
(including a new version of this one), so I'm happy to punt on this for
now and see if it happens again after I've applied the latest versions
of all patches.
-TimS
>> 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,
>>>
>>