[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