[Swan-dev] pluto: Add RFC7383 fragmentation support

Paul Wouters paul at nohats.ca
Thu Apr 30 17:55:17 EEST 2015


On Thu, 30 Apr 2015, Herbert Xu wrote:

> Subject: [Swan-dev] pluto: Add RFC7383 fragmentation support
> 
> This patch adds RFC7383 IKEv2 fragmentation support to pluto.

Wow! Thanks for doing all that work to implement fragmentation!

I'll spin this into a branch and run it through our full test suite and
add some (interop) test cases for it. So it will take a little longer
before it lands into master but we're going to put this into the 3.13
release if we can!

Paul

> Signed-off-by: Herbert Xu <herbert at gondor.apana.org.au>
>
> diff --git a/include/ietf_constants.h b/include/ietf_constants.h
> index e56683d..95fb27a 100644
> --- a/include/ietf_constants.h
> +++ b/include/ietf_constants.h
> @@ -529,6 +529,11 @@ enum next_payload_types_ikev2 {
> 	ISAKMP_NEXT_v2SK = 46,	/* Encrypted payload */
> 	ISAKMP_NEXT_v2CP = 47,	/* Configuration Payload (MODECFG) */
> 	ISAKMP_NEXT_v2EAP = 48,	/* Extensible Authentication Payload */
> +	ISAKMP_NEXT_v2GSPM = 49,
> +	ISAKMP_NEXT_v2IDG = 50,
> +	ISAKMP_NEXT_v2GSA = 51,
> +	ISAKMP_NEXT_v2KD = 52,
> +	ISAKMP_NEXT_v2SKF = 53,	/* Encrypted fragment */
> 	/* 128 - 255 Private Use */
> 	/* Cisco/Microsoft proprietary IKE fragmentation - private use for libreswan */
> 	ISAKMP_NEXT_v2IKE_FRAGMENTATION = 132,
> @@ -1415,8 +1420,14 @@ typedef enum {
> 	v2N_IKEV2_MESSAGE_ID_SYNC = 16422, /* RFC-6311 */
> 	v2N_IPSEC_REPLAY_COUNTER_SYNC = 16423, /* RFC-6311 */
> 	v2N_SECURE_PASSWORD_METHODS = 16424, /* RFC-6467 */
> -
> -	/* 16425 - 40969 Unassigned */
> +	v2N_PSK_PERSIST = 16425,
> +	v2N_PSK_CONFIRM = 16426,
> +	v2N_ERX_SUPPORTED = 16427,
> +	v2N_IFOM_CAPABILITY = 16428,
> +	v2N_SENDER_REQUEST_ID = 16429,
> +	v2N_FRAGMENTATION_SUPPORTED = 16430, /* RFC-7383 */
> +
> +	/* 16431 - 40969 Unassigned */
> 	/* 40960 - 65535 Private Use */
> } v2_notification_t;
>
> diff --git a/lib/libswan/constants.c b/lib/libswan/constants.c
> index 33d2143..d8d4c2a 100644
> --- a/lib/libswan/constants.c
> +++ b/lib/libswan/constants.c
> @@ -286,6 +286,11 @@ const char *const payload_name_ikev2_main[] = {
> 	"ISAKMP_NEXT_v2SK",
> 	"ISAKMP_NEXT_v2CP",
> 	"ISAKMP_NEXT_v2EAP",
> +	"ISAKMP_NEXT_v2GSPM",
> +	"ISAKMP_NEXT_v2IDG",
> +	"ISAKMP_NEXT_v2GSA",
> +	"ISAKMP_NEXT_v2KD",
> +	"ISAKMP_NEXT_v2SKF",
> 	NULL	/* termination for bitnamesof() */
> };
>
> @@ -306,7 +311,7 @@ static enum_names payload_names_ikev2_private_use = {
>
> static enum_names payload_names_ikev2_main = {
> 	ISAKMP_NEXT_v2SA,
> -	ISAKMP_NEXT_v2EAP,
> +	ISAKMP_NEXT_v2SKF,
> 	payload_name_ikev2_main,
> 	&payload_names_ikev2_private_use
> };
> @@ -321,7 +326,7 @@ enum_names ikev2_payload_names = {
> /* either V1 or V2 payload kind */
> static enum_names payload_names_ikev2copy_main = {
> 	ISAKMP_NEXT_v2SA,
> -	ISAKMP_NEXT_v2EAP,
> +	ISAKMP_NEXT_v2SKF,
> 	payload_name_ikev2_main,
> 	&payload_names_ikev1_private_use
> };
> @@ -1603,12 +1608,18 @@ static const char *const ikev2_notify_name_16384[] = {
> 	"v2N_IPSEC_REPLAY_COUNTER_SYNC_SUPPORTED",
> 	"v2N_IKEV2_MESSAGE_ID_SYNC",
> 	"v2N_IPSEC_REPLAY_COUNTER_SYNC",
> -	"v2N_SECURE_PASSWORD_METHODS",    /* 16423 */
> +	"v2N_SECURE_PASSWORD_METHODS",
> +	"v2N_PSK_PERSIST",
> +	"v2N_PSK_CONFIRM",
> +	"v2N_ERX_SUPPORTED",
> +	"v2N_IFOM_CAPABILITY",
> +	"v2N_SENDER_REQUEST_ID",
> +	"v2N_FRAGMENTATION_SUPPORTED",    /* 16430 */
> };
>
> static enum_names ikev2_notify_names_16384 = {
> 	v2N_INITIAL_CONTACT,
> -	v2N_SECURE_PASSWORD_METHODS,
> +	v2N_FRAGMENTATION_SUPPORTED,
> 	ikev2_notify_name_16384,
> 	NULL
> };
> diff --git a/programs/pluto/ikev2.c b/programs/pluto/ikev2.c
> index fcd094f..d1f4c68 100644
> --- a/programs/pluto/ikev2.c
> +++ b/programs/pluto/ikev2.c
> @@ -566,6 +566,7 @@ struct ikev2_payloads_summary ikev2_decode_payloads(struct msg_digest *md,
>
> 		switch (np) {
> 		case ISAKMP_NEXT_v2SK:
> +		case ISAKMP_NEXT_v2SKF:
> 			/* RFC 5996 2.14 "Encrypted Payload":
> 			 *
> 			 * Next Payload - The payload type of the
> @@ -635,6 +636,108 @@ void ikev2_log_payload_errors(struct ikev2_payload_errors errors)
> 	}
> }
>
> +static bool ikev2_check_fragment(struct msg_digest *md)
> +{
> +	struct state *st = md->st;
> +	struct ikev2_skf *skf = &md->chain[ISAKMP_NEXT_v2SKF]->payload.v2skf;
> +	struct ikev2_frag *i;
> +
> +	if (!(st->st_connection->policy & POLICY_IKE_FRAG_ALLOW)) {
> +		libreswan_log("discarding IKE encrypted fragment - fragmentation not allowed by local policy (ike_frag=no)");
> +		return TRUE;
> +	}
> +
> +	DBG(DBG_CONTROL,
> +	    DBG_log("received IKE encrypted fragment number '%u', total number '%u', next payload '%u'",
> +		    skf->isaskf_number, skf->isaskf_total, skf->isaskf_np));
> +
> +	if (!skf->isaskf_number || !skf->isaskf_total ||
> +	    skf->isaskf_number > skf->isaskf_total ||
> +	    (skf->isaskf_number == 1 ? !skf->isaskf_np : skf->isaskf_np)) {
> +		libreswan_log("Ignoring invalid IKE encrypted fragment");
> +		return TRUE;
> +	}
> +
> +	for (i = st->ikev2_frags; i; i = i->next) {
> +		if (i->index != skf->isaskf_number)
> +			continue;
> +
> +		if (i->total == skf->isaskf_total) {
> +			libreswan_log("Ignoring duplicate IKE encrypted fragment");
> +			return TRUE;
> +		}
> +
> +		if (i->total > skf->isaskf_total) {
> +			libreswan_log("Ignoring odd IKE encrypted fragment");
> +			return TRUE;
> +		}
> +	}
> +
> +	return FALSE;
> +}
> +
> +static bool ikev2_collect_fragment(struct msg_digest *md)
> +{
> +	struct state *st = md->st;
> +	struct ikev2_skf *skf = &md->chain[ISAKMP_NEXT_v2SKF]->payload.v2skf;
> +	pb_stream *e_pbs = &md->chain[ISAKMP_NEXT_v2SKF]->pbs;
> +	struct ikev2_frag *frag, **i;
> +	int num_frags;
> +
> +	if (ikev2_check_fragment(md))
> +		return TRUE;
> +
> +	frag = alloc_thing(struct ikev2_frag, "ikev2_frag");
> +	frag->np = skf->isaskf_np;
> +	frag->index = skf->isaskf_number;
> +	frag->total = skf->isaskf_total;
> +	frag->iv = e_pbs->cur - md->packet_pbs.start;
> +	clonetochunk(frag->cipher, md->packet_pbs.start,
> +		     e_pbs->roof - md->packet_pbs.start,
> +		     "IKEv2 encrypted fragment");
> +
> +	/* Add the fragment to the state */
> +	i = &st->ikev2_frags;
> +	if (*i && (*i)->total < skf->isaskf_total)
> +		do {
> +			struct ikev2_frag *old = *i;
> +
> +			(*i) = old->next;
> +			freeanychunk(old->cipher);
> +			pfree(old);
> +		} while (*i);
> +
> +	num_frags = 0;
> +	for (;;) {
> +		if (frag) {
> +			/* Still looking for a place to insert frag */
> +			if (!*i || (*i)->index > frag->index) {
> +				frag->next = *i;
> +				*i = frag;
> +				frag = NULL;
> +			}
> +		}
> +
> +		if (!*i)
> +			break;
> +
> +		num_frags++;
> +
> +		i = &(*i)->next;
> +	}
> +
> +	if (num_frags < skf->isaskf_total)
> +		return TRUE;
> +
> +	/* optimize: if receiving fragments, immediately respond with fragments too */
> +	st->st_seen_fragments = TRUE;
> +
> +	DBG(DBG_CONTROL,
> +	    DBG_log(" updated IKE fragment state to respond using fragments without waiting for re-transmits"));
> +
> +	return FALSE;
> +}
> +
> /*
>  * process an input packet, possibly generating a reply.
>  *
> @@ -822,8 +925,6 @@ void process_v2_packet(struct msg_digest **mdp)
> 	struct ikev2_payloads_summary clear_payload_summary = { .status = STF_IGNORE };
> 	struct ikev2_payload_errors clear_payload_status = { .status = STF_OK };
>
> -	struct ikev2_payloads_summary enc_payload_summary =  { .status = STF_IGNORE };
> -	struct ikev2_payload_errors enc_payload_status = { .status = STF_OK };
>
> 	for (svm = v2_state_microcode_table; svm->state != STATE_IKEv2_ROOF;
> 	     svm++) {
> @@ -858,6 +959,17 @@ void process_v2_packet(struct msg_digest **mdp)
> 				complete_v2_state_transition(mdp, clear_payload_summary.status);
> 				return;
> 			}
> +
> +			if (clear_payload_summary.seen &
> +			    LELEM(PINDEX(ISAKMP_NEXT_v2SKF)))
> +				clear_payload_summary.seen ^=
> +					LELEM(PINDEX(ISAKMP_NEXT_v2SK)) |
> +					LELEM(PINDEX(ISAKMP_NEXT_v2SKF));
> +			if (clear_payload_summary.repeated &
> +			    LELEM(PINDEX(ISAKMP_NEXT_v2SKF)))
> +				clear_payload_summary.repeated ^=
> +					LELEM(PINDEX(ISAKMP_NEXT_v2SK)) |
> +					LELEM(PINDEX(ISAKMP_NEXT_v2SKF));
> 		}
> 		struct ikev2_payload_errors clear_payload_errors
> 			= ikev2_verify_payloads(clear_payload_summary, svm, FALSE);
> @@ -866,40 +978,6 @@ void process_v2_packet(struct msg_digest **mdp)
> 			clear_payload_status = clear_payload_errors;
> 			continue;
> 		}
> -		/*
> -		 * Continue checking by unpacking the SK payload but
> -		 * only its ok and there's one to unpack.  Otherwise
> -		 * assume its a correct match.
> -		 */
> -		if (!(svm->flags & SMF2_UNPACK_SK)) {
> -			break;
> -		}
> -		if (!(svm->req_clear_payloads & LELEM(PINDEX(ISAKMP_NEXT_v2SK)))) {
> -			break;
> -		}
> -		if (enc_payload_summary.status != STF_OK) {
> -			DBG(DBG_CONTROL,
> -			    DBG_log("Unpacking encrypted payload for svm: %s",
> -				    svm->story));
> -			stf_status status = ikev2_verify_and_decrypt_sk_payload(md);
> -			if (status != STF_OK) {
> -				complete_v2_state_transition(mdp, status);
> -				return;
> -			}
> -			unsigned np = md->chain[ISAKMP_NEXT_v2SK]->payload.generic.isag_np;
> -			enc_payload_summary = ikev2_decode_payloads(md, &md->clr_pbs, np);
> -			if (enc_payload_summary.status != STF_OK) {
> -				complete_v2_state_transition(mdp, enc_payload_summary.status);
> -				return;
> -			}
> -		}
> -		struct ikev2_payload_errors enc_payload_errors
> -			= ikev2_verify_payloads(enc_payload_summary, svm, TRUE);
> -		if (enc_payload_errors.status != STF_OK) {
> -			/* Save this failure for later logging. */
> -			enc_payload_status = enc_payload_errors;
> -			continue;
> -		}
> 		/* must be the right state machine entry */
> 		break;
> 	}
> @@ -917,9 +995,6 @@ void process_v2_packet(struct msg_digest **mdp)
> 		if (clear_payload_status.status != STF_OK) {
> 			ikev2_log_payload_errors(clear_payload_status);
> 			complete_v2_state_transition(mdp, clear_payload_status.status);
> -		} else if (enc_payload_status.status != STF_OK) {
> -			ikev2_log_payload_errors(enc_payload_status);
> -			complete_v2_state_transition(mdp, enc_payload_status.status);
> 		} else if (!(md->hdr.isa_flags & ISAKMP_FLAGS_v2_MSG_R)) {
> 			/*
> 			 * We are the responder to this message so
> @@ -936,6 +1011,9 @@ void process_v2_packet(struct msg_digest **mdp)
> 	if (state_busy(st))
> 		return;
>
> +	if (md->chain[ISAKMP_NEXT_v2SKF] && ikev2_collect_fragment(md))
> +		return;
> +
> 	DBG(DBG_PARSING, {
> 		    if (pbs_left(&md->message_pbs) != 0)
> 			    DBG_log("removing %d bytes of padding",
> @@ -1368,9 +1446,6 @@ static void success_v2_state_transition(struct msg_digest *md)
>
> 	/* if requested, send the new reply packet */
> 	if (svm->flags & SMF2_REPLY) {
> -		/* free previously transmitted packet */
> -		freeanychunk(st->st_tpacket);
> -
> 		if (nat_traversal_enabled && from_state != STATE_PARENT_I1) {
> 			/* adjust our destination port if necessary */
> 			nat_traversal_change_port_lookup(md, st);
> @@ -1384,11 +1459,6 @@ static void success_v2_state_transition(struct msg_digest *md)
> 				    st->st_interface->port);
> 		    });
>
> -		close_output_pbs(&reply_stream); /* good form, but actually a no-op */
> -
> -		clonetochunk(st->st_tpacket, reply_stream.start,
> -			     pbs_offset(&reply_stream), "reply packet");
> -
> 		/* actually send the packet
> 		 * Note: this is a great place to implement "impairments"
> 		 * for testing purposes.  Suppress or duplicate the
> diff --git a/programs/pluto/ikev2.h b/programs/pluto/ikev2.h
> index 0b9957e..79eff7a 100644
> --- a/programs/pluto/ikev2.h
> +++ b/programs/pluto/ikev2.h
> @@ -198,8 +198,6 @@ void send_v2_notification_invalid_ke_from_state(struct state *st);
> bool modp_in_propset(oakley_group_t received, struct alg_info_ike *ai_list);
> oakley_group_t first_modp_from_propset(struct alg_info_ike *ai_list);
>
> -stf_status ikev2_verify_and_decrypt_sk_payload(struct msg_digest *md);
> -
> struct ikev2_payloads_summary {
> 	stf_status status;
> 	lset_t seen;
> diff --git a/programs/pluto/ikev2_parent.c b/programs/pluto/ikev2_parent.c
> index 95803a3..a5c6e7c 100644
> --- a/programs/pluto/ikev2_parent.c
> +++ b/programs/pluto/ikev2_parent.c
> @@ -495,6 +495,17 @@ static stf_status ikev2_parent_outI1_common(struct msg_digest *md,
> 		close_output_pbs(&pb);
> 	}
>
> +	/* Send fragmentation support notification */
> +	if (c->policy & POLICY_IKE_FRAG_ALLOW) {
> +		int np = ISAKMP_NEXT_v2N;
> +
> +		if (!ship_v2N(np, ISAKMP_PAYLOAD_NONCRITICAL,
> +			      PROTO_v2_RESERVED, &empty_chunk,
> +			      v2N_FRAGMENTATION_SUPPORTED, &empty_chunk,
> +			      &md->rbody))
> +			return STF_INTERNAL_ERROR;
> +	}
> +
> 	/* Send NAT-T Notify payloads */
> 	{
> 		int np = c->send_vendorid ? ISAKMP_NEXT_v2V : ISAKMP_NEXT_v2NONE;
> @@ -525,6 +536,7 @@ static stf_status ikev2_parent_outI1_common(struct msg_digest *md,
> 	clonetochunk(st->st_tpacket, reply_stream.start,
> 		     pbs_offset(&reply_stream),
> 		     "reply packet for ikev2_parent_outI1_tail");
> +	release_v2fragments(&st->st_tfrags);
>
> 	/* save packet for later signing */
> 	freeanychunk(st->st_firstpacket_me);
> @@ -538,6 +550,26 @@ static stf_status ikev2_parent_outI1_common(struct msg_digest *md,
> 	return STF_OK;
> }
>
> +static void ikev2_check_frag_support(struct msg_digest *md)
> +{
> +	struct payload_digest *p;
> +	struct state *st = md->st;
> +
> +	passert(st);
> +
> +	for (p = md->chain[ISAKMP_NEXT_v2N]; p != NULL; p = p->next) {
> +		switch (p->payload.v2n.isan_type) {
> +		case v2N_FRAGMENTATION_SUPPORTED:
> +			st->st_seen_fragvid = TRUE;
> +			break;
> +		default:
> +			continue;
> +		}
> +
> +		break;
> +	}
> +}
> +
> /*
>  *
>  ***************************************************************
> @@ -788,8 +820,10 @@ stf_status ikev2parent_inI1outR1(struct msg_digest *md)
> 	 * check v2N_NAT_DETECTION_DESTINATION_IP or/and
> 	 * v2N_NAT_DETECTION_SOURCE_IP
> 	 */
> -	if (md->chain[ISAKMP_NEXT_v2N] != NULL)
> +	if (md->chain[ISAKMP_NEXT_v2N] != NULL) {
> 		ikev2_natd_lookup(md, zero_cookie);
> +		ikev2_check_frag_support(md);
> +	}
>
> 	/* calculate the nonce and the KE */
> 	{
> @@ -1008,6 +1042,17 @@ static stf_status ikev2_parent_inI1outR1_tail(
> 			!has_preloaded_public_key(st);
> 	}
>
> +	/* Send fragmentation support notification */
> +	if (c->policy & POLICY_IKE_FRAG_ALLOW) {
> +		int np = ISAKMP_NEXT_v2N;
> +
> +		if (!ship_v2N(np, ISAKMP_PAYLOAD_NONCRITICAL,
> +			      PROTO_v2_RESERVED, &empty_chunk,
> +			      v2N_FRAGMENTATION_SUPPORTED, &empty_chunk,
> +			      &md->rbody))
> +			return STF_INTERNAL_ERROR;
> +	}
> +
> 	/* Send NAT-T Notify payloads */
> 	{
> 		struct ikev2_generic in;
> @@ -1046,6 +1091,7 @@ static stf_status ikev2_parent_inI1outR1_tail(
> 	clonetochunk(st->st_tpacket, reply_stream.start,
> 		     pbs_offset(&reply_stream),
> 		     "reply packet for ikev2_parent_inI1outR1_tail");
> +	release_v2fragments(&st->st_tfrags);
>
> 	/* save packet for later signing */
> 	freeanychunk(st->st_firstpacket_me);
> @@ -1208,6 +1254,7 @@ stf_status ikev2parent_inR1outI2(struct msg_digest *md)
> 		case v2N_USE_TRANSPORT_MODE:
> 		case v2N_NAT_DETECTION_SOURCE_IP:
> 		case v2N_NAT_DETECTION_DESTINATION_IP:
> +		case v2N_FRAGMENTATION_SUPPORTED:
> 			/* we do handle these further down */
> 			break;
> 		default:
> @@ -1255,8 +1302,10 @@ stf_status ikev2parent_inR1outI2(struct msg_digest *md)
> 	/* check v2N_NAT_DETECTION_DESTINATION_IP or/and
> 	 * v2N_NAT_DETECTION_SOURCE_IP
> 	 */
> -	if (md->chain[ISAKMP_NEXT_v2N] != NULL)
> +	if (md->chain[ISAKMP_NEXT_v2N] != NULL) {
> 		ikev2_natd_lookup(md, st->st_rcookie);
> +		ikev2_check_frag_support(md);
> +	}
>
> 	/* initiate calculation of g^xy */
> 	return start_dh_v2(md, "ikev2_inR1outI2 KE", ORIGINAL_INITIATOR,
> @@ -1540,15 +1589,15 @@ static stf_status ikev2_encrypt_msg(struct state *st,
>  * the actual starting-variable (a.k.a. IV).
>  */
>
> -stf_status ikev2_verify_and_decrypt_sk_payload(struct msg_digest *md)
> +static stf_status ikev2_verify_and_decrypt_sk_payload(struct msg_digest *md,
> +						      struct chunk *chunk,
> +						      unsigned int iv)
> {
> 	/* caller should be passing in the original (parent) state. */
> 	struct state *st = md->st;
> 	struct state *pst = IS_CHILD_SA(st) ?
> 		state_with_serialno(st->st_clonedfrom) : st;
>
> -	pb_stream *e_pbs = &md->chain[ISAKMP_NEXT_v2SK]->pbs;
> -
> 	if (st != NULL && !st->hidden_variables.st_skeyid_calculated)
> 	{
> 		DBG(DBG_CRYPT | DBG_CONTROL, {
> @@ -1563,7 +1612,7 @@ stf_status ikev2_verify_and_decrypt_sk_payload(struct msg_digest *md)
> 		return STF_FAIL;
> 	}
>
> -	u_char *wire_iv_start = e_pbs->cur;
> +	u_char *wire_iv_start = chunk->ptr + iv;
> 	size_t wire_iv_size = pst->st_oakley.encrypter->wire_iv_size;
> 	size_t integ_size = (ike_alg_enc_requires_integ(pst->st_oakley.encrypter)
> 			     ? pst->st_oakley.integ_hasher->hash_integ_len
> @@ -1576,14 +1625,14 @@ stf_status ikev2_verify_and_decrypt_sk_payload(struct msg_digest *md)
> 	 * - at least one padding-length byte
> 	 * - truncated integrity digest / tag
> 	 */
> -	u_char *payload_end = e_pbs->roof;
> +	u_char *payload_end = chunk->ptr + chunk->len;
> 	if (payload_end < (wire_iv_start + wire_iv_size + 1 + integ_size)) {
> 		libreswan_log("encrypted payload impossibly short (%zu)",
> 			      payload_end - wire_iv_start);
> 		return STF_FAIL;
> 	}
>
> -	u_char *auth_start = md->packet_pbs.start;
> +	u_char *auth_start = chunk->ptr;
> 	u_char *enc_start = wire_iv_start + wire_iv_size;
> 	u_char *integ_start = payload_end - integ_size;
> 	size_t enc_size = integ_start - enc_start;
> @@ -1705,7 +1754,57 @@ stf_status ikev2_verify_and_decrypt_sk_payload(struct msg_digest *md)
> 	/* don't bother to check any other pad octets */
> 	DBG(DBG_CRYPT, DBG_log("striping %u bytes as pad", padlen));
>
> -	init_pbs(&md->clr_pbs, enc_start, enc_size - padlen, "cleartext");
> +	setchunk(*chunk, enc_start, enc_size - padlen);
> +	return STF_OK;
> +}
> +
> +static stf_status ikev2_reassemble_fragments(struct msg_digest *md,
> +					     struct chunk *chunk)
> +{
> +	struct state *st = md->st;
> +	struct ikev2_frag *frag;
> +	stf_status status;
> +	unsigned int size;
> +	unsigned int offset;
> +
> +	size = 0;
> +	for (frag = st->ikev2_frags; frag; frag = frag->next) {
> +		setchunk(frag->plain, frag->cipher.ptr, frag->cipher.len);
> +
> +		status = ikev2_verify_and_decrypt_sk_payload(
> +			md, &frag->plain, frag->iv);
> +		if (status != STF_OK) {
> +			release_v2fragments(&st->ikev2_frags);
> +			return status;
> +		}
> +
> +		size += frag->plain.len;
> +	}
> +
> +	/* We have all the fragments */
> +	md->raw_packet.ptr = alloc_bytes(size, "IKE fragments buffer");
> +
> +	/* Reassemble fragments in buffer */
> +	frag = st->ikev2_frags;
> +	md->chain[ISAKMP_NEXT_v2SKF]->payload.v2skf.isaskf_np = frag->np;
> +	offset = 0;
> +	do {
> +		struct ikev2_frag *old = frag;
> +
> +		passert(offset + frag->plain.len <= size);
> +		memcpy(md->raw_packet.ptr + offset, frag->plain.ptr,
> +		       frag->plain.len);
> +		offset += frag->plain.len;
> +		frag = frag->next;
> +
> +		freeanychunk(old->cipher);
> +		pfree(old);
> +	} while (frag);
> +
> +	st->ikev2_frags = NULL;
> +
> +	setchunk(*chunk, md->raw_packet.ptr, size);
> +
> 	return STF_OK;
> }
>
> @@ -1713,11 +1812,29 @@ static
> stf_status ikev2_decrypt_msg(struct msg_digest *md)
> {
> 	stf_status status;
> -	status = ikev2_verify_and_decrypt_sk_payload(md);
> +	struct chunk chunk;
> +
> +	if (md->chain[ISAKMP_NEXT_v2SKF]) {
> +		status = ikev2_reassemble_fragments(md, &chunk);
> +	} else {
> +		pb_stream *e_pbs = &md->chain[ISAKMP_NEXT_v2SK]->pbs;
> +
> +		setchunk(chunk, md->packet_pbs.start,
> +			 e_pbs->roof - md->packet_pbs.start);
> +
> +		status = ikev2_verify_and_decrypt_sk_payload(
> +			md, &chunk, e_pbs->cur - md->packet_pbs.start);
> +	}
> +
> 	if (status != STF_OK) {
> 		return status;
> 	}
> -	unsigned np = md->chain[ISAKMP_NEXT_v2SK]->payload.generic.isag_np;
> +
> +	init_pbs(&md->clr_pbs, chunk.ptr, chunk.len, "cleartext");
> +
> +	unsigned np = md->chain[ISAKMP_NEXT_v2SK] ?
> +		      md->chain[ISAKMP_NEXT_v2SK]->payload.generic.isag_np :
> +		      md->chain[ISAKMP_NEXT_v2SKF]->payload.v2skf.isaskf_np;
> 	struct ikev2_payloads_summary summary = ikev2_decode_payloads(md, &md->clr_pbs, np);
> 	if (summary.status != STF_OK) {
> 		return status;
> @@ -1849,6 +1966,158 @@ static stf_status ikev2_send_auth(struct connection *c,
> 	return STF_OK;
> }
>
> +static stf_status ikev2_send_fragment(struct msg_digest *md,
> +				      struct isakmp_hdr *hdr,
> +				      struct ikev2_generic *oe,
> +				      struct ikev2_frag **fragp,
> +				      struct chunk *payload,
> +				      unsigned int count, unsigned int total,
> +				      const char *desc)
> +{
> +	struct state *st = md->st;
> +	struct ikev2_skf e;
> +	unsigned char *encstart;
> +	pb_stream e_pbs, e_pbs_cipher;
> +	unsigned char *iv;
> +	unsigned char *authstart;
> +
> +	/* make sure HDR is at start of a clean buffer */
> +	zero(&reply_buffer);
> +	init_pbs(&reply_stream, reply_buffer, sizeof(reply_buffer),
> +		 "reply packet");
> +
> +	/* beginning of data going out */
> +	authstart = reply_stream.cur;
> +
> +	/* HDR out */
> +	{
> +		hdr->isa_np = ISAKMP_NEXT_v2SKF;
> +
> +		if (!out_struct(hdr, &isakmp_hdr_desc, &reply_stream,
> +				&md->rbody))
> +			return STF_INTERNAL_ERROR;
> +	}
> +
> +	/* insert an Encryption payload header */
> +	e.isaskf_np = count == 1 ? oe->isag_np : 0;
> +	e.isaskf_critical = oe->isag_critical;
> +	e.isaskf_number = count;
> +	e.isaskf_total = total;
> +
> +	if (!out_struct(&e, &ikev2_skf_desc, &md->rbody, &e_pbs))
> +		return STF_INTERNAL_ERROR;
> +
> +	/* insert IV */
> +	iv = e_pbs.cur;
> +	if (!emit_wire_iv(st, &e_pbs))
> +		return STF_INTERNAL_ERROR;
> +
> +	/* note where cleartext starts */
> +	init_pbs(&e_pbs_cipher, e_pbs.cur, e_pbs.roof - e_pbs.cur,
> +		 "cleartext");
> +	e_pbs_cipher.container = &e_pbs;
> +	e_pbs_cipher.desc = NULL;
> +	e_pbs_cipher.cur = e_pbs.cur;
> +	encstart = e_pbs_cipher.cur;
> +
> +	if (!out_raw(payload->ptr, payload->len, &e_pbs_cipher,
> +		     "cleartext fragment"))
> +		return STF_INTERNAL_ERROR;
> +
> +	/*
> +	 * need to extend the packet so that we will know how big it is
> +	 * since the length is under the integrity check
> +	 */
> +	if (!ikev2_padup_pre_encrypt(st, &e_pbs_cipher))
> +		return STF_INTERNAL_ERROR;
> +
> +	close_output_pbs(&e_pbs_cipher);
> +
> +	{
> +		unsigned char *authloc = ikev2_authloc(st, &e_pbs);
> +		int ret;
> +
> +		if (authloc == NULL)
> +			return STF_INTERNAL_ERROR;
> +
> +		close_output_pbs(&e_pbs);
> +		close_output_pbs(&md->rbody);
> +		close_output_pbs(&reply_stream);
> +
> +		ret = ikev2_encrypt_msg(st, authstart,
> +					iv, encstart, authloc,
> +					&e_pbs, &e_pbs_cipher);
> +		if (ret != STF_OK)
> +			return ret;
> +	}
> +
> +	*fragp = alloc_thing(struct ikev2_frag, "ikev2_frag");
> +	clonetochunk((*fragp)->cipher, reply_stream.start,
> +		     pbs_offset(&reply_stream), desc);
> +
> +	return STF_OK;
> +}
> +
> +static stf_status ikev2_send_fragments(struct msg_digest *md,
> +				       struct isakmp_hdr *hdr,
> +				       struct ikev2_generic *e,
> +				       struct chunk *payload,
> +				       const char *desc)
> +{
> +	struct state *st = md->st;
> +	unsigned int len;
> +	unsigned int offset;
> +	unsigned int total;
> +	unsigned int count;
> +	struct ikev2_frag **fragp;
> +	int ret;
> +
> +	len = (st->st_connection->addr_family == AF_INET) ?
> +	      ISAKMP_FRAG_MAXLEN_IPv4 : ISAKMP_FRAG_MAXLEN_IPv6;
> +
> +	if (st->st_interface && st->st_interface->ike_float)
> +		len -= NON_ESP_MARKER_SIZE;
> +
> +	len -= NSIZEOF_isakmp_hdr + NSIZEOF_isakmp_ikefrag;
> +	len -= ike_alg_enc_requires_integ(st->st_oakley.encrypter) ?
> +	       st->st_oakley.integ_hasher->hash_integ_len :
> +	       st->st_oakley.encrypter->aead_tag_size;
> +
> +	if (st->st_oakley.encrypter->pad_to_blocksize)
> +		len &= ~(st->st_oakley.encrypter->enc_blocksize - 1);
> +
> +	len -= 2;
> +
> +	total = (payload->len - 1) / len + 1;
> +	if (total >= 128) {
> +		loglog(RC_LOG_SERIOUS, "Too many frags %u %u",
> +		       (unsigned int)payload->len, len);
> +		return STF_INTERNAL_ERROR;
> +	}
> +
> +	count = 1;
> +	offset = 0;
> +	fragp = &st->st_tfrags;
> +
> +	for (;;) {
> +		struct chunk cipher;
> +
> +		if (offset + len > payload->len)
> +			len = payload->len - offset;
> +		setchunk(cipher, payload->ptr + offset, len);
> +		offset += len;
> +		ret = ikev2_send_fragment(md, hdr, e, fragp, &cipher,
> +					  count++, total, desc);
> +
> +		if (ret != STF_OK || offset >= payload->len)
> +			break;
> +
> +		fragp = &(*fragp)->next;
> +	}
> +
> +	return ret;
> +}
> +
> static stf_status ikev2_parent_inR1outI2_tail(
> 	struct pluto_crypto_req_cont *dh,
> 	struct pluto_crypto_req *r)
> @@ -1865,6 +2134,9 @@ static stf_status ikev2_parent_inR1outI2_tail(
> 	unsigned char *authstart;
> 	struct state *pst = st;
> 	bool send_cert = FALSE;
> +	struct isakmp_hdr hdr = md->hdr;
> +	unsigned char *authloc;
> +	unsigned int len;
>
> 	finish_dh_v2(st, r);
>
> @@ -1906,8 +2178,6 @@ static stf_status ikev2_parent_inR1outI2_tail(
>
> 	/* HDR out */
> 	{
> -		struct isakmp_hdr hdr = md->hdr;
> -
> 		/* clear all flags, set original initiator */
> 		hdr.isa_flags = ISAKMP_FLAGS_v2_IKE_I;
> 		hdr.isa_version = build_ikev2_version();
> @@ -2073,6 +2343,8 @@ static stf_status ikev2_parent_inR1outI2_tail(
> 		}
> 	}
>
> +	len = pbs_offset(&e_pbs_cipher);
> +
> 	/*
> 	 * need to extend the packet so that we will know how big it is
> 	 * since the length is under the integrity check
> @@ -2082,15 +2354,29 @@ static stf_status ikev2_parent_inR1outI2_tail(
>
> 	close_output_pbs(&e_pbs_cipher);
>
> -	{
> -		unsigned char *authloc = ikev2_authloc(st, &e_pbs);
> +	authloc = ikev2_authloc(st, &e_pbs);
>
> -		if (authloc == NULL)
> -			return STF_INTERNAL_ERROR;
> +	if (authloc == NULL)
> +		return STF_INTERNAL_ERROR;
>
> -		close_output_pbs(&e_pbs);
> -		close_output_pbs(&md->rbody);
> -		close_output_pbs(&reply_stream);
> +	close_output_pbs(&e_pbs);
> +	close_output_pbs(&md->rbody);
> +	close_output_pbs(&reply_stream);
> +
> +	if (should_fragment_ike_msg(st, pbs_offset(&reply_stream), TRUE)) {
> +		struct chunk payload;
> +
> +		clonetochunk(payload, e_pbs_cipher.start, len,
> +			     "cleartext for ikev2_parent_outR1_I2");
> +
> +		ret = ikev2_send_fragments(md, &hdr, &e, &payload,
> +					   "reply fragment for ikev2_parent_outR1_I2");
> +
> +		freeanychunk(payload);
> +		return ret;
> +	}
> +
> +	{
>
> 		ret = ikev2_encrypt_msg(st, authstart,
> 					iv, encstart, authloc,
> @@ -2106,6 +2392,7 @@ static stf_status ikev2_parent_inR1outI2_tail(
> 	clonetochunk(pst->st_tpacket, reply_stream.start,
> 		     pbs_offset(&reply_stream),
> 		     "reply packet for ikev2_parent_outI1");
> +	release_v2fragments(&pst->st_tfrags);
>
> 	return STF_OK;
> }
> @@ -2588,10 +2875,13 @@ static stf_status ikev2_parent_inI2outR2_auth_tail( struct msg_digest *md,
> 	{
> 		unsigned char *encstart;
> 		unsigned char *iv;
> +		unsigned char *authloc;
> 		struct ikev2_generic e;
> 		pb_stream e_pbs, e_pbs_cipher;
> 		stf_status ret;
> 		bool send_cert = FALSE;
> +		unsigned int len;
> +		struct isakmp_hdr hdr;
>
> 		/* make sure HDR is at start of a clean buffer */
> 		zero(&reply_buffer);
> @@ -2600,7 +2890,7 @@ static stf_status ikev2_parent_inI2outR2_auth_tail( struct msg_digest *md,
>
> 		/* HDR out */
> 		{
> -			struct isakmp_hdr hdr = md->hdr; /* grab cookies */
> +			hdr = md->hdr; /* grab cookies */
>
> 			/* set msg responder flag - clear others */
> 			hdr.isa_flags = ISAKMP_FLAGS_v2_MSG_R;
> @@ -2749,22 +3039,37 @@ static stf_status ikev2_parent_inI2outR2_auth_tail( struct msg_digest *md,
> 			}
> 		}
>
> +		len = pbs_offset(&e_pbs_cipher);
> +
> 		if (!ikev2_padup_pre_encrypt(st, &e_pbs_cipher))
> 			return STF_INTERNAL_ERROR;
>
> 		close_output_pbs(&e_pbs_cipher);
>
> -		{
> -			unsigned char *authloc = ikev2_authloc(st, &e_pbs);
> +		authloc = ikev2_authloc(st, &e_pbs);
>
> -			if (authloc == NULL)
> -				return STF_INTERNAL_ERROR;
> +		if (authloc == NULL)
> +			return STF_INTERNAL_ERROR;
>
> -			close_output_pbs(&e_pbs);
> +		close_output_pbs(&e_pbs);
> +		close_output_pbs(&md->rbody);
> +		close_output_pbs(&reply_stream);
>
> -			close_output_pbs(&md->rbody);
> -			close_output_pbs(&reply_stream);
> +		if (should_fragment_ike_msg(st, pbs_offset(&reply_stream),
> +						TRUE)) {
> +			struct chunk payload;
> +
> +			clonetochunk(payload, e_pbs_cipher.start, len,
> +				     "cleartext for ikev2_parent_inI2outR2_tail");
>
> +			ret = ikev2_send_fragments(md, &hdr, &e, &payload,
> +						   "reply fragment for ikev2_parent_inI2outR2_tail");
> +
> +			freeanychunk(payload);
> +			return ret;
> +		}
> +
> +		{
> 			ret = ikev2_encrypt_msg(st, authstart,
> 						iv, encstart, authloc,
> 						&e_pbs, &e_pbs_cipher);
> @@ -2778,6 +3083,7 @@ static stf_status ikev2_parent_inI2outR2_auth_tail( struct msg_digest *md,
> 	clonetochunk(st->st_tpacket, reply_stream.start,
> 		     pbs_offset(&reply_stream),
> 		     "reply packet for ikev2_parent_inI2outR2_tail");
> +	release_v2fragments(&st->st_tfrags);
>
> 	/* note: retransmission is driven by initiator */
>
> @@ -3311,6 +3617,7 @@ void send_v2_notification(struct state *p1st,
>
> 	clonetochunk(p1st->st_tpacket, reply_stream.start, pbs_offset(&reply_stream),
> 		     "notification packet");
> +	release_v2fragments(&p1st->st_tfrags);
>
> 	send_ike_msg(p1st, __FUNCTION__);
> }
> @@ -3599,6 +3906,7 @@ static stf_status ikev2_child_inIoutR_tail(struct pluto_crypto_req_cont *qke,
>
> 	freeanychunk(pst->st_tpacket);
> 	clonetochunk(pst->st_tpacket, reply_stream.start, pbs_offset(&reply_stream), "reply packet for CREATE_CHILD_SA exchange");
> +	release_v2fragments(&pst->st_tfrags);
>
> 	send_ike_msg(pst, __FUNCTION__);
>
> @@ -3942,6 +4250,7 @@ stf_status process_encrypted_informational_ikev2(struct msg_digest *md)
> 		clonetochunk(st->st_tpacket, reply_stream.start,
> 			     pbs_offset(&reply_stream),
> 			     "reply packet for informational exchange");
> +		release_v2fragments(&st->st_tfrags);
>
> 		send_ike_msg(st, __FUNCTION__);
> 	}
> @@ -4189,6 +4498,7 @@ stf_status ikev2_send_informational(struct state *st)
> 		clonetochunk(pst->st_tpacket, reply_stream.start,
> 			     pbs_offset(&reply_stream),
> 			     "reply packet for informational exchange");
> +		release_v2fragments(&st->st_tfrags);
> 		pst->st_pend_liveness = TRUE; /* we should only do this when dpd/liveness is active? */
> 		send_ike_msg(pst, __FUNCTION__);
> 	}
> @@ -4345,6 +4655,7 @@ static bool ikev2_delete_out_guts(struct state *const st, struct state *const ps
> 	clonetochunk(pst->st_tpacket, reply_stream.start,
> 		     pbs_offset(&reply_stream),
> 		     "request packet for informational exchange");
> +	release_v2fragments(&pst->st_tfrags);
>
> 	send_ike_msg(pst, __FUNCTION__);
>
> diff --git a/programs/pluto/packet.c b/programs/pluto/packet.c
> index 896f14f..b6d4a2d 100644
> --- a/programs/pluto/packet.c
> +++ b/programs/pluto/packet.c
> @@ -1194,6 +1194,41 @@ struct_desc ikev2_sk_desc = { "IKEv2 Encryption Payload",
> 			     ikev2generic_fields,
> 			     sizeof(struct ikev2_generic) };
>
> +/*
> + * 2.5.  Fragmenting Message
> + *
> + *                         1                   2                   3
> + *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
> + *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *    | Next Payload  |C|  RESERVED   |         Payload Length        |
> + *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *    |        Fragment Number        |        Total Fragments        |
> + *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *    |                     Initialization Vector                     |
> + *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *    ~                      Encrypted content                        ~
> + *    +               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *    |               |             Padding (0-255 octets)            |
> + *    +-+-+-+-+-+-+-+-+                               +-+-+-+-+-+-+-+-+
> + *    |                                               |  Pad Length   |
> + *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *    ~                    Integrity Checksum Data                    ~
> + *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *
> + *                         Encrypted Fragment Payload
> + */
> +static field_desc ikev2skf_fields[] = {
> +	{ ft_enum, 8 / BITS_PER_BYTE, "next payload type", &ikev2_payload_names },
> +	{ ft_set, 8 / BITS_PER_BYTE, "flags", critical_names },
> +	{ ft_len, 16 / BITS_PER_BYTE, "length", NULL },
> +	{ ft_nat, 16 / BITS_PER_BYTE, "fragment number", NULL },
> +	{ ft_nat, 16 / BITS_PER_BYTE, "total fragments", NULL },
> +	{ ft_end,  0, NULL, NULL }
> +};
> +
> +struct_desc ikev2_skf_desc = { "IKEv2 Encrypted Fragment",
> +			      ikev2skf_fields, sizeof(struct ikev2_skf) };
> +
> /* descriptor for each V1 payload type
>  *
>  * There is a slight problem in that some payloads differ, depending
> @@ -1242,7 +1277,13 @@ static struct_desc *const v2_payload_descs[] = {
> 	&ikev2_ts_desc,			/* 44 ISAKMP_NEXT_v2TSi */
> 	&ikev2_ts_desc,			/* 45 ISAKMP_NEXT_v2TSr */
> 	&ikev2_sk_desc,                 /* 46 ISAKMP_NEXT_v2SK */
> -	&ikev2_cp_desc,			/* 57 ISAKMP_NEXT_v2CP */
> +	&ikev2_cp_desc,			/* 47 ISAKMP_NEXT_v2CP */
> +	NULL,				/* 48 */
> +	NULL,				/* 49 */
> +	NULL,				/* 50 */
> +	NULL,				/* 51 */
> +	NULL,				/* 52 */
> +	&ikev2_skf_desc,                /* 53 ISAKMP_NEXT_v2SKF */
> };
>
> static field_desc suggested_group_fields[] = {
> diff --git a/programs/pluto/packet.h b/programs/pluto/packet.h
> index 15af444..1e0e6ce 100644
> --- a/programs/pluto/packet.h
> +++ b/programs/pluto/packet.h
> @@ -902,6 +902,19 @@ struct ikev2_cp_attribute {
>
> extern struct_desc ikev2_cp_attribute_desc;
>
> +/*
> + * Fragment Message. RFC 7383 section 2.5
> + */
> +struct ikev2_skf {
> +	u_int8_t isaskf_np;
> +	u_int8_t isaskf_critical;
> +	u_int16_t isaskf_length;
> +	u_int16_t isaskf_number;
> +	u_int16_t isaskf_total;
> +};
> +
> +extern struct_desc ikev2_skf_desc;
> +
> /* union of all payloads */
>
> union payload {
> @@ -931,6 +944,7 @@ union payload {
> 	struct ikev2_delete v2delete;
> 	struct ikev2_cp v2cp;
> 	struct ikev2_cp_attribute v2cp_attribute;
> +	struct ikev2_skf v2skf;
> };
>
> struct suggested_group {
> diff --git a/programs/pluto/server.c b/programs/pluto/server.c
> index ed287c2..7e9693c 100644
> --- a/programs/pluto/server.c
> +++ b/programs/pluto/server.c
> @@ -1215,6 +1215,32 @@ static bool send_frags(struct state *st, const char *where)
> 	return TRUE;
> }
>
> +bool should_fragment_ike_msg(struct state *st, size_t len, bool resending)
> +{
> +	if (st->st_interface && st->st_interface->ike_float)
> +		len += NON_ESP_MARKER_SIZE;
> +
> +	return len >= (st->st_connection->addr_family == AF_INET ?
> +		       ISAKMP_FRAG_MAXLEN_IPv4 : ISAKMP_FRAG_MAXLEN_IPv6) &&
> +	       (resending ?
> +		(st->st_connection->policy & POLICY_IKE_FRAG_ALLOW) &&
> +		st->st_seen_fragvid :
> +		(st->st_connection->policy & POLICY_IKE_FRAG_FORCE) ||
> +		st->st_seen_fragments);
> +}
> +
> +static bool send_ikev2_frags(struct state *st, const char *where)
> +{
> +	struct ikev2_frag *frag;
> +
> +	for (frag = st->st_tfrags; frag; frag = frag->next)
> +		if (!send_packet(st, where, FALSE,
> +				 frag->cipher.ptr, frag->cipher.len, NULL, 0))
> +			return FALSE;
> +
> +	return TRUE;
> +}
> +
> static bool send_or_resend_ike_msg(struct state *st, const char *where,
> 				   bool resending)
> {
> @@ -1234,15 +1260,10 @@ static bool send_or_resend_ike_msg(struct state *st, const char *where,
> 	/* decide of whether we're to fragment  - IKEv1 only, draft-smyslov-ipsecme-ikev2-fragmentation not implemented yet */
> 	if (!st->st_ikev2 &&
> 	    st->st_state != STATE_MAIN_I1 &&
> -	    len + natt_bonus >=
> -		(st->st_connection->addr_family == AF_INET ?
> -		 ISAKMP_FRAG_MAXLEN_IPv4 : ISAKMP_FRAG_MAXLEN_IPv6) &&
> -	    ((resending &&
> -	      (st->st_connection->policy & POLICY_IKE_FRAG_ALLOW) &&
> -	      st->st_seen_fragvid) ||
> -	     ((st->st_connection->policy & POLICY_IKE_FRAG_FORCE) ||
> -	      st->st_seen_fragments))) {
> +	    should_fragment_ike_msg(st, len + natt_bonus, resending)) {
> 		return send_frags(st, where);
> +	} else if (st->st_ikev2 && st->st_tfrags) {
> +		return send_ikev2_frags(st, where);
> 	} else {
> 		return send_packet(st, where, FALSE, st->st_tpacket.ptr,
> 				   st->st_tpacket.len, NULL, 0);
> diff --git a/programs/pluto/server.h b/programs/pluto/server.h
> index ddec566..09ac047 100644
> --- a/programs/pluto/server.h
> +++ b/programs/pluto/server.h
> @@ -85,5 +85,7 @@ extern struct event *pluto_event_new(evutil_socket_t ft, short events,
> bool ev_before(struct pluto_event *pev, deltatime_t delay);
> extern void set_pluto_busy(bool busy);
> extern void set_whack_pluto_ddos(enum ddos_mode mode);
> +extern bool should_fragment_ike_msg(struct state *st, size_t len,
> +				    bool resending);
>
> #endif /* _SERVER_H */
> diff --git a/programs/pluto/state.c b/programs/pluto/state.c
> index a720845..d77907c 100644
> --- a/programs/pluto/state.c
> +++ b/programs/pluto/state.c
> @@ -454,10 +454,31 @@ void release_whack(struct state *st)
> 	close_any(st->st_whack_sock);
> }
>
> +void release_v2fragments(struct ikev2_frag **fragp)
> +{
> +	struct ikev2_frag *frag = *fragp;
> +
> +	while (frag != NULL) {
> +		struct ikev2_frag *this = frag;
> +
> +		frag = this->next;
> +		freeanychunk(this->cipher);
> +
> +		pfree(this);
> +	}
> +
> +	*fragp = NULL;
> +}
> +
> void release_fragments(struct state *st)
> {
> 	struct ike_frag *frag = st->ike_frags;
>
> +	if (st->st_ikev2) {
> +		release_v2fragments(&st->ikev2_frags);
> +		return;
> +	}
> +
> 	while (frag != NULL) {
> 		struct ike_frag *this = frag;
>
> @@ -664,6 +685,7 @@ void delete_state(struct state *st)
> 	freeanychunk(st->st_firstpacket_me);
> 	freeanychunk(st->st_firstpacket_him);
> 	freeanychunk(st->st_tpacket);
> +	release_v2fragments(&st->st_tfrags);
> 	freeanychunk(st->st_rpacket);
> 	freeanychunk(st->st_p1isa);
> 	freeanychunk(st->st_gi);
> diff --git a/programs/pluto/state.h b/programs/pluto/state.h
> index 64a7955..9cb3c97 100644
> --- a/programs/pluto/state.h
> +++ b/programs/pluto/state.h
> @@ -149,6 +149,16 @@ struct ike_frag {
> 	size_t size;
> };
>
> +struct ikev2_frag {
> +	struct ikev2_frag *next;
> +	int np;
> +	int index;
> +	int total;
> +	unsigned int iv;
> +	struct chunk cipher;
> +	struct chunk plain;
> +};
> +
> /*
>  * internal state that
>  * should get copied by god... to the child SA state.
> @@ -230,7 +240,11 @@ struct state {
> 	const char        *st_suspended_md_func;
> 	int st_suspended_md_line;
>
> -	struct ike_frag *ike_frags;		/* collected ike fragments */
> +	/* collected ike fragments */
> +	union {
> +		struct ike_frag *ike_frags;
> +		struct ikev2_frag *ikev2_frags;
> +	};
>
> 	struct trans_attrs st_oakley;
>
> @@ -315,6 +329,7 @@ struct state {
>
> 	/* my stuff */
> 	chunk_t st_tpacket;                     /* Transmitted packet */
> +	struct ikev2_frag *st_tfrags;		/* Transmitted fragments */
>
> #ifdef HAVE_LABELED_IPSEC
> 	struct xfrm_user_sec_ctx_ike *sec_ctx;
> @@ -545,6 +560,7 @@ extern void fmt_state(struct state *st, const monotime_t n,
> extern void delete_states_by_peer(const ip_address *peer);
> extern void replace_states_by_peer(const ip_address *peer);
> extern void release_fragments(struct state *st);
> +extern void release_v2fragments(struct ikev2_frag **fragp);
> extern void v1_delete_state_by_xauth_name(struct state *st, void *name);
> extern void delete_state_by_id_name(struct state *st, void *name);
>
> -- 
> Email: Herbert Xu <herbert at gondor.apana.org.au>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
> _______________________________________________
> Swan-dev mailing list
> Swan-dev at lists.libreswan.org
> https://lists.libreswan.org/mailman/listinfo/swan-dev
>




More information about the Swan-dev mailing list