Tim Stewart <t...@stoo.org> 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, >>> >>