From 80318cb73f9fe1670dff6eb1801ec47bbf0ff285 Mon Sep 17 00:00:00 2001
From: Apple <opensource@apple.com>
Date: Thu, 17 Jun 2010 16:16:37 +0000
Subject: [PATCH] ipsec-93.8.tar.gz

---
 ipsec-tools/racoon/handler.c      |  15 ++-
 ipsec-tools/racoon/ike_session.c  | 198 +++++++++++++++++++++++++++++-
 ipsec-tools/racoon/ike_session.h  |   4 +
 ipsec-tools/racoon/isakmp.c       |   4 +
 ipsec-tools/racoon/isakmp_agg.c   |   5 +-
 ipsec-tools/racoon/isakmp_base.c  |   8 +-
 ipsec-tools/racoon/isakmp_ident.c |   8 +-
 ipsec-tools/racoon/isakmp_inf.c   |   2 +-
 ipsec-tools/racoon/isakmp_quick.c |  36 ++++--
 ipsec-tools/racoon/nattraversal.c |  10 +-
 ipsec-tools/racoon/pfkey_racoon.c |  18 ++-
 ipsec-tools/racoon/proposal.c     | 149 ++++++++++++++++++----
 ipsec-tools/racoon/proposal.h     |   1 +
 13 files changed, 415 insertions(+), 43 deletions(-)

diff --git a/ipsec-tools/racoon/handler.c b/ipsec-tools/racoon/handler.c
index 9dc7f92..3eb3a0b 100644
--- a/ipsec-tools/racoon/handler.c
+++ b/ipsec-tools/racoon/handler.c
@@ -780,7 +780,9 @@ flushph2(int ignore_established_handles)
 
 	for (p = LIST_FIRST(&ph2tree); p; p = next) {
 		next = LIST_NEXT(p, chain);
-		
+		if (p->is_dying || p->status == PHASE2ST_EXPIRED) {
+			continue;
+		}
 		if (p->status == PHASE2ST_ESTABLISHED){
 			if (ignore_established_handles) {
 				plog(LLV_DEBUG2, LOCATION, NULL,
@@ -820,6 +822,9 @@ deleteallph2(src, dst, proto_id)
 
 	for (iph2 = LIST_FIRST(&ph2tree); iph2 != NULL; iph2 = next) {
 		next = LIST_NEXT(iph2, chain);
+		if (iph2->is_dying || iph2->status == PHASE2ST_EXPIRED) {
+			continue;
+		}
 		if (iph2->proposal == NULL && iph2->approval == NULL)
 			continue;
 		if (cmpsaddrwop(src, iph2->src) != 0 ||
@@ -1325,12 +1330,15 @@ purgephXbyspid(u_int32_t spid,
     // do ph2's first... we need the ph1s for notifications
 	LIST_FOREACH(iph2, &ph2tree, chain) {
 		if (spid == iph2->spid) {
+			if (iph2->is_dying || iph2->status == PHASE2ST_EXPIRED) {
+				continue;
+			}
             if (iph2->status == PHASE2ST_ESTABLISHED) {
                 isakmp_info_send_d2(iph2);
             }
-            isakmp_ph2expire(iph2); // iph2 will go down 1 second later.
             ike_session_stopped_by_controller(iph2->parent_session,
                                               ike_session_stopped_by_flush);
+            isakmp_ph2expire(iph2); // iph2 will go down 1 second later.
         }
     }
 
@@ -1339,6 +1347,9 @@ purgephXbyspid(u_int32_t spid,
 		if (spid == iph2->spid) {
             if (del_boundph1 && iph2->parent_session) {
                 for (iph1 = LIST_FIRST(&iph2->parent_session->ikev1_state.ph1tree); iph1; iph1 = LIST_NEXT(iph1, ph1ofsession_chain)) {
+					if (iph1->is_dying || iph1->status == PHASE1ST_EXPIRED) {
+						continue;
+					}
                     if (iph1->status == PHASE1ST_ESTABLISHED) {
                         isakmp_info_send_d1(iph1);
                     }
diff --git a/ipsec-tools/racoon/ike_session.c b/ipsec-tools/racoon/ike_session.c
index ffc1516..97e5be7 100644
--- a/ipsec-tools/racoon/ike_session.c
+++ b/ipsec-tools/racoon/ike_session.c
@@ -904,7 +904,7 @@ ike_session_cleanup_other_established_ph1s (ike_session_t    *session,
 		 * TODO: currently, most recently established SA wins. Need to revisit to see if 
 		 * alternative selections is better (e.g. largest p->index stays).
 		 */
-		if (p != new_iph1) {
+		if (p != new_iph1 && !p->is_dying) {
 			SCHED_KILL(p->sce);
 			SCHED_KILL(p->sce_rekey);
 			p->is_dying = 1;
@@ -943,6 +943,10 @@ ike_session_cleanup_ph2 (struct ph2handle *iph2)
 
     SCHED_KILL(iph2->sce);
 
+	plog(LLV_ERROR, LOCATION, NULL,
+		 "about to cleanup ph2: status %d, seq %d dying %d\n",
+		 iph2->status, iph2->seq, iph2->is_dying);
+	
 	/* send delete information */
 	if (iph2->status == PHASE2ST_ESTABLISHED) {
 		isakmp_info_send_d2(iph2);
@@ -993,7 +997,7 @@ ike_session_cleanup_other_established_ph2s (ike_session_t    *session,
 		 * TODO: currently, most recently established SA wins. Need to revisit to see if 
 		 * alternative selections is better.
 		 */
-		if (p != new_iph2 && p->spid == new_iph2->spid) {
+		if (p != new_iph2 && p->spid == new_iph2->spid && !p->is_dying) {
 			SCHED_KILL(p->sce);
 			p->is_dying = 1;
 			
@@ -1067,6 +1071,9 @@ ike_session_purge_ph2s_by_ph1 (struct ph1handle *iph1)
 	for (p = LIST_FIRST(&iph1->parent_session->ikev1_state.ph2tree); p; p = next) {
 		// take next pointer now, since delete change the underlying ph2tree list
 		next = LIST_NEXT(p, ph2ofsession_chain);
+		if (p->is_dying) {
+			continue;
+		}
         SCHED_KILL(p->sce);
         p->is_dying = 1;
 			
@@ -1401,6 +1408,47 @@ ike_session_is_id_ipany (vchar_t *ext_id)
 	return 0;
 }
 
+static int
+ike_session_is_id_portany (vchar_t *ext_id)
+{
+	struct id {
+		u_int8_t type;		/* ID Type */
+		u_int8_t proto_id;	/* Protocol ID */
+		u_int16_t port;		/* Port */
+		u_int32_t addr;		/* IPv4 address */
+		u_int32_t mask;
+	} *id_ptr;
+	
+	/* ignore addr */
+	id_ptr = (struct id *)ext_id->v;
+	if (id_ptr->type == IPSECDOI_ID_IPV4_ADDR &&
+	    id_ptr->port == 0) {
+		return 1;
+	}
+	plog(LLV_DEBUG2, LOCATION, NULL, "not portany_ids in %s: type %d, port %x.\n",
+		 __FUNCTION__, id_ptr->type, id_ptr->port);
+	return 0;
+}
+
+static void
+ike_session_set_id_portany (vchar_t *ext_id)
+{
+	struct id {
+		u_int8_t type;		/* ID Type */
+		u_int8_t proto_id;	/* Protocol ID */
+		u_int16_t port;		/* Port */
+		u_int32_t addr;		/* IPv4 address */
+		u_int32_t mask;
+	} *id_ptr;
+	
+	/* ignore addr */
+	id_ptr = (struct id *)ext_id->v;
+	if (id_ptr->type == IPSECDOI_ID_IPV4_ADDR) {
+	    id_ptr->port = 0;
+		return;
+	}
+}
+
 static int
 ike_session_cmp_ph2_ids_ipany (vchar_t *ext_id,
 							   vchar_t *ext_id_p)
@@ -1412,10 +1460,19 @@ ike_session_cmp_ph2_ids_ipany (vchar_t *ext_id,
 	return 0;
 }
 
-static int
+/*
+ * ipsec rekeys for l2tp-over-ipsec fail particularly when client is behind nat because the client's configs and policies don't 
+ * match the server's view of the client's address and port.
+ * servers behave differently when using this address-port info to generate ids during phase2 rekeys, so try to match the incoming id to 
+ * a variety of info saved in the older phase2.
+ */
+int
 ike_session_cmp_ph2_ids (struct ph2handle *iph2,
 						 struct ph2handle *older_ph2)
 {
+	vchar_t *portany_id = NULL;
+	vchar_t *portany_id_p = NULL;
+
 	if (iph2->id && older_ph2->id &&
 	    iph2->id->l == older_ph2->id->l &&
 	    memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
@@ -1440,6 +1497,82 @@ ike_session_cmp_ph2_ids (struct ph2handle *iph2,
 	    memcmp(iph2->id_p->v, older_ph2->ext_nat_id_p->v, iph2->id_p->l) == 0) {
 		return 0;
 	}
+	if (iph2->id && older_ph2->ext_nat_id &&
+	    iph2->id->l == older_ph2->ext_nat_id->l &&
+	    memcmp(iph2->id->v, older_ph2->ext_nat_id->v, iph2->id->l) == 0 &&
+	    iph2->id_p && older_ph2->id_p &&
+	    iph2->id_p->l == older_ph2->id_p->l &&
+	    memcmp(iph2->id_p->v, older_ph2->id_p->v, iph2->id_p->l) == 0) {
+		return 0;
+	}
+	if (iph2->id && older_ph2->id &&
+	    iph2->id->l == older_ph2->id->l &&
+	    memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
+	    iph2->id_p && older_ph2->ext_nat_id_p &&
+	    iph2->id_p->l == older_ph2->ext_nat_id_p->l &&
+	    memcmp(iph2->id_p->v, older_ph2->ext_nat_id_p->v, iph2->id_p->l) == 0) {
+		return 0;
+	}
+
+	/* check if the external id has a wildcard port and compare ids accordingly */
+	if ((older_ph2->ext_nat_id && ike_session_is_id_portany(older_ph2->ext_nat_id)) ||
+		(older_ph2->ext_nat_id_p && ike_session_is_id_portany(older_ph2->ext_nat_id_p))) {
+		// try ignoring ports in iph2->id and iph2->id
+		if (iph2->id && (portany_id = vdup(iph2->id))) {
+			ike_session_set_id_portany(portany_id);
+		}
+		if (iph2->id_p && (portany_id_p = vdup(iph2->id_p))) {
+			ike_session_set_id_portany(portany_id_p);
+		}
+		if (portany_id && older_ph2->ext_nat_id &&
+			portany_id->l == older_ph2->ext_nat_id->l &&
+			memcmp(portany_id->v, older_ph2->ext_nat_id->v, portany_id->l) == 0 &&
+			portany_id_p && older_ph2->ext_nat_id_p &&
+			portany_id_p->l == older_ph2->ext_nat_id_p->l &&
+			memcmp(portany_id_p->v, older_ph2->ext_nat_id_p->v, portany_id_p->l) == 0) {
+			if (portany_id) {
+				vfree(portany_id);
+			}
+			if (portany_id_p) {
+				vfree(portany_id_p);
+			}
+			return 0;
+		}
+		if (iph2->id && older_ph2->ext_nat_id &&
+			iph2->id->l == older_ph2->ext_nat_id->l &&
+			memcmp(portany_id->v, older_ph2->ext_nat_id->v, portany_id->l) == 0 &&
+			iph2->id_p && older_ph2->id_p &&
+			iph2->id_p->l == older_ph2->id_p->l &&
+			memcmp(iph2->id_p->v, older_ph2->id_p->v, iph2->id_p->l) == 0) {
+			if (portany_id) {
+				vfree(portany_id);
+			}
+			if (portany_id_p) {
+				vfree(portany_id_p);
+			}
+			return 0;
+		}
+		if (iph2->id && older_ph2->id &&
+			iph2->id->l == older_ph2->id->l &&
+			memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
+			iph2->id_p && older_ph2->ext_nat_id_p &&
+			iph2->id_p->l == older_ph2->ext_nat_id_p->l &&
+			memcmp(portany_id_p->v, older_ph2->ext_nat_id_p->v, portany_id_p->l) == 0) {
+			if (portany_id) {
+				vfree(portany_id);
+			}
+			if (portany_id_p) {
+				vfree(portany_id_p);
+			}
+			return 0;
+		}
+		if (portany_id) {
+			vfree(portany_id);
+		}
+		if (portany_id_p) {
+			vfree(portany_id_p);
+		}
+	}
 	return -1;
 }
 
@@ -1461,6 +1594,18 @@ ike_session_get_sainfo_r (struct ph2handle *iph2)
 				    ike_session_cmp_ph2_ids(iph2, p) == 0) {
 					plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 matched in %s.\n", __FUNCTION__);
 					iph2->sainfo = p->sainfo;
+					if (p->ext_nat_id) {
+						if (iph2->ext_nat_id) {
+							vfree(iph2->ext_nat_id);
+						}
+						iph2->ext_nat_id = vdup(p->ext_nat_id);
+					}
+					if (p->ext_nat_id_p) {
+						if (iph2->ext_nat_id_p) {
+							vfree(iph2->ext_nat_id_p);
+						}
+						iph2->ext_nat_id_p = vdup(p->ext_nat_id_p);
+					}
 					return 0;
 				}
 			}
@@ -1469,6 +1614,53 @@ ike_session_get_sainfo_r (struct ph2handle *iph2)
 	return -1;
 }
 
+int
+ike_session_get_proposal_r (struct ph2handle *iph2)
+{
+	if (iph2->parent_session &&
+	    iph2->parent_session->is_client &&
+	    iph2->id && iph2->id_p) {
+		struct ph2handle *p;
+		int ipany_ids = ike_session_cmp_ph2_ids_ipany(iph2->id, iph2->id_p);
+		plog(LLV_DEBUG2, LOCATION, NULL, "ipany_ids %d in %s.\n", ipany_ids, __FUNCTION__);
+
+		for (p = LIST_FIRST(&iph2->parent_session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
+			if (iph2 != p && !p->is_dying && p->status >= PHASE2ST_ESTABLISHED &&
+			    p->approval) {
+				plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 found in %s.\n", __FUNCTION__);
+				if (ipany_ids ||
+				    ike_session_cmp_ph2_ids(iph2, p) == 0) {
+					plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 matched in %s.\n", __FUNCTION__);
+					iph2->proposal = dupsaprop(p->approval, 1);
+					return 0;
+				}
+			}
+		}
+	}
+	return -1;
+}
+
+void
+ike_session_update_natt_version (struct ph1handle *iph1)
+{
+	if (iph1->parent_session) {
+		if (iph1->natt_options) {
+			iph1->parent_session->natt_version = iph1->natt_options->version;
+		} else {
+			iph1->parent_session->natt_version = 0;
+		}
+	}
+}
+
+int
+ike_session_get_natt_version (struct ph1handle *iph1)
+{
+	if (iph1->parent_session) {
+		return(iph1->parent_session->natt_version);
+	}
+	return 0;
+}
+
 int
 ike_session_drop_rekey (ike_session_t *session)
 {
diff --git a/ipsec-tools/racoon/ike_session.h b/ipsec-tools/racoon/ike_session.h
index 4d4854c..bc018d6 100644
--- a/ipsec-tools/racoon/ike_session.h
+++ b/ipsec-tools/racoon/ike_session.h
@@ -98,6 +98,7 @@ struct ike_session {
     int					 is_client:1;
     time_t                               last_time_data_sc_detected;
     u_int32_t                            natt_flags;
+	u_int32_t                            natt_version;
 	char                                *term_reason;
 
 	struct timeval						 start_timestamp;
@@ -153,6 +154,9 @@ extern int                ike_session_is_client_ph1_rekey __P((struct ph1handle
 extern void               ike_session_start_xauth_timer __P((struct ph1handle *));
 extern void               ike_session_stop_xauth_timer __P((struct ph1handle *));
 extern int                ike_session_get_sainfo_r __P((struct ph2handle *));
+extern int                ike_session_get_proposal_r __P((struct ph2handle *));
+extern void               ike_session_update_natt_version __P((struct ph1handle *));
+extern int                ike_session_get_natt_version __P((struct ph1handle *));
 extern int                ike_session_drop_rekey __P((ike_session_t *));
 extern void               ike_session_ph2_retransmits __P((struct ph2handle *));
 
diff --git a/ipsec-tools/racoon/isakmp.c b/ipsec-tools/racoon/isakmp.c
index f499850..050ca42 100644
--- a/ipsec-tools/racoon/isakmp.c
+++ b/ipsec-tools/racoon/isakmp.c
@@ -2618,6 +2618,10 @@ isakmp_ph2expire(iph2)
 {
 	char *src, *dst;
 
+	if (iph2->status == PHASE2ST_EXPIRED) {
+		return;
+	}
+
 	SCHED_KILL(iph2->sce);
 
 	src = racoon_strdup(saddrwop2str(iph2->src));
diff --git a/ipsec-tools/racoon/isakmp_agg.c b/ipsec-tools/racoon/isakmp_agg.c
index 7dddea3..d2c59b7 100644
--- a/ipsec-tools/racoon/isakmp_agg.c
+++ b/ipsec-tools/racoon/isakmp_agg.c
@@ -631,6 +631,7 @@ agg_i2recv(iph1, msg)
 
 		if (iph1->natt_flags & NAT_DETECTED)
 			natt_float_ports (iph1);
+		ike_session_update_natt_version(iph1);
 	}
 #endif
 
@@ -1106,10 +1107,12 @@ agg_r1recv(iph1, msg)
 	}
 
 #ifdef ENABLE_NATT
-	if (NATT_AVAILABLE(iph1))
+	if (NATT_AVAILABLE(iph1)) {
 		plog(LLV_INFO, LOCATION, iph1->remote,
 		     "Selected NAT-T version: %s\n",
 		     vid_string_by_id(iph1->natt_options->version));
+		ike_session_update_natt_version(iph1);
+	}
 #endif
 
 	/* check SA payload and set approval SA for use */
diff --git a/ipsec-tools/racoon/isakmp_base.c b/ipsec-tools/racoon/isakmp_base.c
index 3ac9e7c..5a26c50 100644
--- a/ipsec-tools/racoon/isakmp_base.c
+++ b/ipsec-tools/racoon/isakmp_base.c
@@ -397,10 +397,12 @@ base_i2recv(iph1, msg)
 	}
 
 #ifdef ENABLE_NATT
-	if (NATT_AVAILABLE(iph1))
+	if (NATT_AVAILABLE(iph1)) {
 		plog(LLV_INFO, LOCATION, iph1->remote,
 		     "Selected NAT-T version: %s\n",
 		     vid_string_by_id(iph1->natt_options->version));
+		ike_session_update_natt_version(iph1);
+	}
 #endif
 
 	/* check SA payload and set approval SA for use */
@@ -938,10 +940,12 @@ base_r1recv(iph1, msg)
 	}
 
 #ifdef ENABLE_NATT
-	if (NATT_AVAILABLE(iph1))
+	if (NATT_AVAILABLE(iph1)) {
 		plog(LLV_INFO, LOCATION, iph1->remote,
 		     "Selected NAT-T version: %s\n",
 		     vid_string_by_id(iph1->natt_options->version));
+		ike_session_update_natt_version(iph1);
+	}
 #endif
 
 	/* check SA payload and set approval SA for use */
diff --git a/ipsec-tools/racoon/isakmp_ident.c b/ipsec-tools/racoon/isakmp_ident.c
index 6488207..ff155e6 100644
--- a/ipsec-tools/racoon/isakmp_ident.c
+++ b/ipsec-tools/racoon/isakmp_ident.c
@@ -364,10 +364,12 @@ ident_i2recv(iph1, msg)
 	}
 
 #ifdef ENABLE_NATT
-	if (NATT_AVAILABLE(iph1))
+	if (NATT_AVAILABLE(iph1)) {
 		plog(LLV_INFO, LOCATION, iph1->remote,
 		     "Selected NAT-T version: %s\n",
 		     vid_string_by_id(iph1->natt_options->version));
+		ike_session_update_natt_version(iph1);
+	}
 #endif
 
 	/* check SA payload and set approval SA for use */
@@ -1190,10 +1192,12 @@ ident_r1recv(iph1, msg)
 	}
 
 #ifdef ENABLE_NATT
-	if (NATT_AVAILABLE(iph1))
+	if (NATT_AVAILABLE(iph1)) {
 		plog(LLV_INFO, LOCATION, iph1->remote,
 		     "Selected NAT-T version: %s\n",
 		     vid_string_by_id(iph1->natt_options->version));
+		ike_session_update_natt_version(iph1);
+	}
 #endif
 
 	/* check SA payload and set approval SA for use */
diff --git a/ipsec-tools/racoon/isakmp_inf.c b/ipsec-tools/racoon/isakmp_inf.c
index 44fa30f..003e3ce 100644
--- a/ipsec-tools/racoon/isakmp_inf.c
+++ b/ipsec-tools/racoon/isakmp_inf.c
@@ -1447,7 +1447,7 @@ purge_isakmp_spi(proto, spi, n)
 
 	for (i = 0; i < n; i++) {
 		iph1 = getph1byindex(&spi[i]);
-		if (!iph1)
+		if (!iph1 || iph1->is_dying || iph1->status == PHASE1ST_EXPIRED)
 			continue;
 
 		plog(LLV_INFO, LOCATION, NULL,
diff --git a/ipsec-tools/racoon/isakmp_quick.c b/ipsec-tools/racoon/isakmp_quick.c
index 44f8fed..2b73f65 100644
--- a/ipsec-tools/racoon/isakmp_quick.c
+++ b/ipsec-tools/racoon/isakmp_quick.c
@@ -97,7 +97,8 @@
 /* quick mode */
 static vchar_t *quick_ir1mx __P((struct ph2handle *, vchar_t *, vchar_t *));
 static int get_sainfo_r __P((struct ph2handle *));
-static int get_proposal_r __P((struct ph2handle *, int));
+static int get_proposal_r __P((struct ph2handle *));
+static int get_proposal_r_remote __P((struct ph2handle *, int));
 
 /* %%%
  * Quick Mode
@@ -568,6 +569,7 @@ quick_i2recv(iph2, msg0)
 								goto end;
 							}
 							plog(LLV_DEBUG, LOCATION, NULL, "external nat address saved.\n");
+							plogdump(LLV_DEBUG, iph2->ext_nat_id->v, iph2->ext_nat_id->l);
 						} else if (f_id && (iph2->ph1->natt_flags & NAT_DETECTED_PEER)) {
 							if (iph2->ext_nat_id_p)
 								vfree(iph2->ext_nat_id_p);
@@ -578,6 +580,7 @@ quick_i2recv(iph2, msg0)
 							}
 							memcpy(iph2->ext_nat_id_p->v, &(idp_ptr->b), iph2->ext_nat_id_p->l);
 							plog(LLV_DEBUG, LOCATION, NULL, "peer's external nat address saved.\n");
+							plogdump(LLV_DEBUG, iph2->ext_nat_id_p->v, iph2->ext_nat_id_p->l);
 						} 
 					} else {
 						plog(LLV_ERROR, LOCATION, NULL, "mismatched ID was returned.\n");
@@ -1369,12 +1372,7 @@ quick_r1recv(iph2, msg0)
 	}
 
     /* check the existence of ID payload and create responder's proposal */
-    error = get_proposal_r(iph2, 0);
-    if (error != -2 && error != 0 && 
-		(((iph2->ph1->natt_flags & NAT_DETECTED_ME) && lcconf->ext_nat_id != NULL) ||
-		 (iph2->parent_session && iph2->parent_session->is_client)))
-        error = get_proposal_r(iph2, 1);
-		
+	error = get_proposal_r(iph2);
 	switch (error) {
 	case -2:
 		/* generate a policy template from peer's proposal */
@@ -1602,6 +1600,8 @@ quick_r2send(iph2, msg)
 	}
 #endif
 
+	plog(LLV_DEBUG, LOCATION, NULL, "Approved SA\n");
+	printsaprop0(LLV_DEBUG, iph2->approval);
 
 	body = vmalloc(tlen);
 	if (body == NULL) { 
@@ -1634,9 +1634,13 @@ quick_r2send(iph2, msg)
 	if (iph2->id_p != NULL) {
 		/* IDci */
 		p = set_isakmp_payload(p, iph2->id_p, ISAKMP_NPTYPE_ID);
+		plog(LLV_DEBUG, LOCATION, NULL, "sending IDci2:\n");
+		plogdump(LLV_DEBUG, iph2->id_p->v, iph2->id_p->l);
 		/* IDcr */
 		np_p = &((struct isakmp_gen *)p)->np;	/* XXX */
 		p = set_isakmp_payload(p, iph2->id, (natoa_type ? natoa_type : ISAKMP_NPTYPE_NONE));
+		plog(LLV_DEBUG, LOCATION, NULL, "sending IDcr2:\n");
+		plogdump(LLV_DEBUG, iph2->id->v, iph2->id->l);
 	}
 
 	/* add a RESPONDER-LIFETIME notify payload if needed */
@@ -2369,6 +2373,22 @@ end:
 	return error;
 }
 
+static int
+get_proposal_r(iph2)
+	struct ph2handle *iph2;
+{
+	int error = get_proposal_r_remote(iph2, 0);
+	if (error != -2 && error != 0 && 
+		(((iph2->ph1->natt_flags & NAT_DETECTED_ME) && lcconf->ext_nat_id != NULL) ||
+		 (iph2->parent_session && iph2->parent_session->is_client))) {
+		if (iph2->parent_session && iph2->parent_session->is_client)
+			error = ike_session_get_proposal_r(iph2);
+		if (error != -2 && error != 0)
+			error = get_proposal_r_remote(iph2, 1);					
+	}
+	return error;
+}
+
 /*
  * Copy both IP addresses in ID payloads into [src,dst]_id if both ID types
  * are IP address and same address family.
@@ -2380,7 +2400,7 @@ end:
  * NOTE: This function is only for responder.
  */
 static int
-get_proposal_r(iph2, use_remote_addr)
+get_proposal_r_remote(iph2, use_remote_addr)
 	struct ph2handle *iph2;
 	int use_remote_addr;
 {
diff --git a/ipsec-tools/racoon/nattraversal.c b/ipsec-tools/racoon/nattraversal.c
index 3a57fdb..4dfd089 100644
--- a/ipsec-tools/racoon/nattraversal.c
+++ b/ipsec-tools/racoon/nattraversal.c
@@ -496,6 +496,8 @@ natt_float_ports (struct ph1handle *iph1)
 void
 natt_handle_vendorid (struct ph1handle *iph1, int vid_numeric)
 {
+  int version;
+
   if (! iph1->natt_options)
     iph1->natt_options = racoon_calloc (1, sizeof (*iph1->natt_options));
 
@@ -504,7 +506,13 @@ natt_handle_vendorid (struct ph1handle *iph1, int vid_numeric)
 	  "Allocating memory for natt_options failed!\n");
     return;
   }
-  
+
+  // stick to the version we already selected on a previous phase1
+  version = ike_session_get_natt_version(iph1);
+  if (version) {
+    vid_numeric = version;
+  }
+
   if (iph1->natt_options->version < vid_numeric)
     if (natt_fill_options (iph1->natt_options, vid_numeric) == 0)
       iph1->natt_flags |= NAT_ANNOUNCED;
diff --git a/ipsec-tools/racoon/pfkey_racoon.c b/ipsec-tools/racoon/pfkey_racoon.c
index 4ff5db3..ef64f60 100644
--- a/ipsec-tools/racoon/pfkey_racoon.c
+++ b/ipsec-tools/racoon/pfkey_racoon.c
@@ -998,6 +998,13 @@ pk_recvgetspi(mhp)
 		return -1;
 	}
 
+	if (iph2->is_dying) {
+		plog(LLV_ERROR, LOCATION, NULL,
+			 "status mismatch phase2 dying (db:%d msg:%d)\n",
+			 iph2->status, PHASE2ST_GETSPISENT);
+		return -1;
+	}
+
 	if (iph2->status != PHASE2ST_GETSPISENT) {
 		plog(LLV_ERROR, LOCATION, NULL,
 			"status mismatch (db:%d msg:%d)\n",
@@ -1340,6 +1347,13 @@ pk_recvupdate(mhp)
 		return -1;
 	}
 
+	if (iph2->is_dying) {
+		plog(LLV_ERROR, LOCATION, NULL,
+			 "status mismatch phase2 dying (db:%d msg:%d)\n",
+			 iph2->status, PHASE2ST_ADDSA);
+		return -1;
+	}
+
 	if (iph2->status != PHASE2ST_ADDSA) {
 		plog(LLV_ERROR, LOCATION, NULL,
 			"status mismatch (db:%d msg:%d)\n",
@@ -1809,7 +1823,7 @@ pk_recvexpire(mhp)
 			    sa_mode));
 		return 0;
 	}
-	if (iph2->status != PHASE2ST_ESTABLISHED) {
+	if (iph2->is_dying || iph2->status != PHASE2ST_ESTABLISHED) {
 		/*
 		 * If the status is not equal to PHASE2ST_ESTABLISHED,
 		 * racoon ignores this expire message.  There are two reason.
@@ -1820,7 +1834,7 @@ pk_recvexpire(mhp)
 		 */
 		plog(LLV_WARNING, LOCATION, NULL,
 			"the expire message is received "
-			"but the handler has not been established.\n");
+			"but the handler is dying or has not been established.\n");
 		return 0;
 	}
 
diff --git a/ipsec-tools/racoon/proposal.c b/ipsec-tools/racoon/proposal.c
index 7ade887..7a299e0 100644
--- a/ipsec-tools/racoon/proposal.c
+++ b/ipsec-tools/racoon/proposal.c
@@ -180,6 +180,50 @@ inssatrns(pr, new)
 	return;
 }
 
+#ifdef ENABLE_NATT
+static void
+saprop_udp_encap (struct saproto *pr)
+{
+	switch (pr->encmode) {
+		case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
+		case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
+			pr->encmode = IPSECDOI_ATTR_ENC_MODE_TUNNEL;
+			pr->udp_encap = 1;
+			break;
+		case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
+		case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
+			pr->encmode = IPSECDOI_ATTR_ENC_MODE_TRNS;
+			pr->udp_encap = 1;
+			break;
+	}
+}
+
+static void
+saprop_adjust_encmode (struct saproto *pr2, struct saproto *pr1)
+{
+	int prev;
+
+	if (natt_udp_encap(pr2->encmode)) {
+		prev = pr2->encmode;
+		saprop_udp_encap(pr2);
+		plog(LLV_INFO, LOCATION, NULL, "Adjusting my encmode %s(%d)->%s(%d)\n",
+			 s_ipsecdoi_encmode(prev),
+			 prev,
+			 s_ipsecdoi_encmode(pr2->encmode),
+			 pr2->encmode);
+	}
+	if (natt_udp_encap(pr1->encmode)) {
+		prev = pr1->encmode;
+		saprop_udp_encap(pr1);
+		plog(LLV_INFO, LOCATION, NULL, "Adjusting peer's encmode %s(%d)->%s(%d)\n",
+			 s_ipsecdoi_encmode(prev),
+			 prev,
+			 s_ipsecdoi_encmode(pr1->encmode),
+			 pr1->encmode);
+	}	
+}
+#endif // ENABLE_NATT
+
 /*
  * take a single match between saprop.  allocate a new proposal and return it
  * for future use (like picking single proposal from a bundle).
@@ -388,27 +432,9 @@ cmpsaprop_alloc(ph1, pp1, pp2, side)
 			goto err;
 		}
 
-#ifdef ENABLE_NATT
-		if ((ph1->natt_flags & NAT_DETECTED) && 
-		    natt_udp_encap (pr2->encmode))
-		{
-			plog(LLV_INFO, LOCATION, NULL, "Adjusting my encmode %s->%s\n",
-			     s_ipsecdoi_encmode(pr2->encmode),
-			     s_ipsecdoi_encmode(pr2->encmode - ph1->natt_options->mode_udp_diff));
-			pr2->encmode -= ph1->natt_options->mode_udp_diff;
-			pr2->udp_encap = 1;
-		}
-
-		if ((ph1->natt_flags & NAT_DETECTED) &&
-		    natt_udp_encap (pr1->encmode))
-		{
-			plog(LLV_INFO, LOCATION, NULL, "Adjusting peer's encmode %s(%d)->%s(%d)\n",
-			     s_ipsecdoi_encmode(pr1->encmode),
-			     pr1->encmode,
-			     s_ipsecdoi_encmode(pr1->encmode - ph1->natt_options->mode_udp_diff),
-			     pr1->encmode - ph1->natt_options->mode_udp_diff);
-			pr1->encmode -= ph1->natt_options->mode_udp_diff;
-			pr1->udp_encap = 1;
+#ifdef ENABLE_NATT		
+		if (ph1->natt_flags & NAT_DETECTED) {
+			saprop_adjust_encmode(pr2, pr1);
 		}
 #endif
 
@@ -1208,3 +1234,84 @@ tunnel_mode_prop(p)
 			return 1;
 	return 0;
 }
+
+void
+dupsatrns(newpr, head)
+	struct saproto *newpr;
+	struct satrns *head;
+{
+	struct satrns *p, *newtr;
+
+	for (p = head; p != NULL; p = p->next) {
+		newtr = newsatrns();
+		if (newtr) {
+			newtr->trns_no = p->trns_no;
+			newtr->trns_id = p->trns_id;
+			newtr->encklen = p->encklen;
+			newtr->authtype = p->authtype;
+			inssatrns(newpr, newtr);
+		} else {
+			break;
+		}
+
+	}
+
+	return;
+}
+	
+void
+dupsaproto(newpp, head, ignore_spis)
+	struct saprop  *newpp;
+	struct saproto *head;
+	int ignore_spis;
+{
+	struct saproto *p, *newpr;
+
+	for (p = head; p != NULL; p = p->next) {
+		newpr = newsaproto();
+		if (newpr) {
+			newpr->proto_id = p->proto_id;
+			newpr->spisize = p->spisize;
+			newpr->encmode = p->encmode;
+			newpr->udp_encap = p->udp_encap;
+			if (!ignore_spis) {
+				newpr->spi = p->spi;
+				newpr->spi_p = p->spi_p;
+				newpr->reqid_in = p->reqid_in;
+				newpr->reqid_out = p->reqid_out;
+			}
+			dupsatrns(newpr, p->head);
+			inssaproto(newpp, newpr);
+		} else {
+			break;
+		}
+
+	}
+
+	return;
+}
+
+struct saprop *
+dupsaprop(head, ignore_spis)
+	struct saprop *head;
+	int ignore_spis;
+{
+	struct saprop *p, *newpp;
+
+	for (p = head, newpp = NULL; p != NULL; p = p->next) {
+		struct saprop *tmp = newsaprop();
+		if (tmp) {
+			tmp->prop_no = p->prop_no;
+			tmp->lifetime = p->lifetime;
+			tmp->lifebyte = p->lifebyte;
+			tmp->pfs_group = p->pfs_group;
+			tmp->claim = p->claim;
+			dupsaproto(tmp, p->head, ignore_spis);
+			inssaprop(&newpp, tmp);
+		} else {
+			break;
+		}
+	}
+
+	return newpp;
+}
diff --git a/ipsec-tools/racoon/proposal.h b/ipsec-tools/racoon/proposal.h
index 72df73a..a9cc8da 100644
--- a/ipsec-tools/racoon/proposal.h
+++ b/ipsec-tools/racoon/proposal.h
@@ -207,5 +207,6 @@ extern int set_proposal_from_policy __P((struct ph2handle *,
 	struct secpolicy *, struct secpolicy *));
 extern int set_proposal_from_proposal __P((struct ph2handle *));
 extern int tunnel_mode_prop __P((struct saprop *));
+extern struct saprop *dupsaprop __P((struct saprop *, int));
 
 #endif /* _PROPOSAL_H */
-- 
2.47.2