Re: Revised notify_delete-2.00.diff, was Re: delete-sa-01 test

"D. Hugh Redelmeier" <[email protected]>
Newsgroups gmane.network.freeswan.devel
Message-ID <[email protected]>
| From: D. Hugh Redelmeier <[email protected]>

| I've not finished reviewing the code, but I've attached the my working
| copy.  It compiles cleanly but has never been run.

I haven't got more time to work on this just now.  I've advanced the
code a bit since the last posting.

Math?  Ken?  Care to take this code back for now?

Thanks,

Hugh Redelmeier
[email protected]  voice: +1 416 482-8253
notify_delete-2.00.diff.DHR02 (text/plain, 38.3 KB)
Index: demux.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/demux.c,v
retrieving revision 1.137
diff -u -r1.137 demux.c
--- demux.c	7 Feb 2003 04:25:35 -0000	1.137
+++ demux.c	13 Feb 2003 07:11:23 -0000
@@ -783,7 +783,6 @@
 static stf_status
 informational(struct msg_digest *md UNUSED)
 {
-    loglog(RC_LOG_SERIOUS, "received and ignored informational message");
     return STF_IGNORE;
 }
 
@@ -1002,9 +1001,9 @@
     /* Clone actual message contents
      * and set up md->packet_pbs to describe it.
      */
-    init_pbs(&md->packet_pbs,
-	clone_bytes(bigbuffer, packet_len, "message buffer in comm_handle()"),
-	packet_len, "packet");
+    init_pbs(&md->packet_pbs
+	, clone_bytes(bigbuffer, packet_len, "message buffer in comm_handle()")
+	, packet_len, "packet");
 
     DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL,
 	{
@@ -1035,12 +1034,33 @@
     struct state *st = NULL;
     enum state_kind from_state = STATE_UNDEFINED;	/* state we started in */
 
+#   define SEND_NOTIFICATION(t) { \
+	if (st == NULL) \
+	    send_notification_from_md(md, t); \
+	else \
+	    send_notification_from_state(st, from_state, t); \
+    }
+
     if (!in_struct(&md->hdr, &isakmp_hdr_desc, &md->packet_pbs, &md->message_pbs))
     {
-	/* XXX specific failures (special notification?):
+	/* Identify specific failures:
 	 * - bad ISAKMP major/minor version numbers
-	 * - size of packet vs size of message
 	 */
+	if (md->packet_pbs.roof - md->packet_pbs.cur >= (ptrdiff_t)isakmp_hdr_desc.size)
+	{
+	    struct isakmp_hdr *hdr = (struct isakmp_hdr *)md->packet_pbs.cur;
+	    if ((hdr->isa_version >> ISA_MAJ_SHIFT) != ISAKMP_MAJOR_VERSION)
+	    {
+		SEND_NOTIFICATION(INVALID_MAJOR_VERSION);
+		return;
+	    }
+	    else if ((hdr->isa_version & ISA_MIN_MASK) != ISAKMP_MINOR_VERSION)
+	    {
+		SEND_NOTIFICATION(INVALID_MINOR_VERSION);
+		return;
+	    }
+	}
+	SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	return;
     }
 
@@ -1048,6 +1068,7 @@
     {
 	log("size (%u) differs from size specified in ISAKMP HDR (%u)"
 	    , (unsigned) pbs_room(&md->packet_pbs), md->hdr.isa_length);
+	SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	return;
     }
 
@@ -1059,18 +1080,18 @@
 #endif
 
     case ISAKMP_XCHG_IDPROT:	/* part of a Main Mode exchange */
-	if (md->hdr.isa_msgid != 0)
+	if (md->hdr.isa_msgid != MAINMODE_MSGID)
 	{
-	    log("Message ID was 0x%08lx but should be zero in Main Mode",
-		(unsigned long) md->hdr.isa_msgid);
-	    /* XXX Could send notification back */
+	    log("Message ID was 0x%08lx but should be zero in Main Mode"
+		, (unsigned long) md->hdr.isa_msgid);
+	    SEND_NOTIFICATION(INVALID_MESSAGE_ID);
 	    return;
 	}
 
 	if (is_zero_cookie(md->hdr.isa_icookie))
 	{
 	    log("Initiator Cookie must not be zero in Main Mode message");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_COOKIE);
 	    return;
 	}
 
@@ -1083,6 +1104,7 @@
 	    {
 		log("initial Main Mode message is invalid:"
 		    " its Encrypted Flag is on");
+		SEND_NOTIFICATION(INVALID_FLAGS);
 		return;
 	    }
 
@@ -1123,7 +1145,7 @@
 
     case ISAKMP_XCHG_INFO:	/* an informational exchange */
 	st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
-	    , &md->sender, 0);
+	    , &md->sender, MAINMODE_MSGID);
 
 	if (st != NULL)
 	    set_cur_state(st);
@@ -1145,7 +1167,7 @@
 		return;
 	    }
 
-	    if (md->hdr.isa_msgid == 0)
+	    if (md->hdr.isa_msgid == MAINMODE_MSGID)
 	    {
 		loglog(RC_LOG_SERIOUS, "Informational Exchange message is invalid because"
 		    " it has a Message ID of 0");
@@ -1185,7 +1207,7 @@
 	{
 	    log("Quick Mode message is invalid because"
 		" it has an Initiator Cookie of 0");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_COOKIE);
 	    return;
 	}
 
@@ -1193,15 +1215,15 @@
 	{
 	    log("Quick Mode message is invalid because"
 		" it has a Responder Cookie of 0");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_COOKIE);
 	    return;
 	}
 
-	if (md->hdr.isa_msgid == 0)
+	if (md->hdr.isa_msgid == MAINMODE_MSGID)
 	{
 	    log("Quick Mode message is invalid because"
 		" it has a Message ID of 0");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_MESSAGE_ID);
 	    return;
 	}
 
@@ -1215,7 +1237,7 @@
 	     * ??? what if this is a duplicate of another message?
 	     */
 	    st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
-		, &md->sender, 0);
+		, &md->sender, MAINMODE_MSGID);
 
 	    if (st == NULL)
 	    {
@@ -1231,7 +1253,7 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "Quick Mode message is unacceptable because"
 		    " it is for an incomplete ISAKMP SA");
-		/* XXX Could send notification back */
+		SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */);
 		return;
 	    }
 
@@ -1242,7 +1264,7 @@
 		    " it uses a previously used Message ID 0x%08lx"
 		    " (perhaps this is a duplicated packet)"
 		    , (unsigned long) md->hdr.isa_msgid);
-		/* XXX Could send notification INVALID_MESSAGE_ID back */
+		SEND_NOTIFICATION(INVALID_MESSAGE_ID);
 		return;
 	    }
 
@@ -1268,6 +1290,7 @@
     default:
 	log("unsupported exchange type %s in message"
 	    , enum_show(&exchange_names, md->hdr.isa_xchg));
+	SEND_NOTIFICATION(UNSUPPORTED_EXCHANGE_TYPE);
 	return;
     }
 
@@ -1364,23 +1387,24 @@
 	if (st == NULL)
 	{
 	    log("discarding encrypted message for an unknown ISAKMP SA");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */);
 	    return;
 	}
 	if (st->st_skeyid_e.ptr == (u_char *) NULL)
 	{
 	    loglog(RC_LOG_SERIOUS, "discarding encrypted message"
 		" because we haven't yet negotiated keying materiel");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_FLAGS);
 	    return;
 	}
 
 	/* Mark as encrypted */
 	md->encrypted = TRUE;
 
-	DBG(DBG_CRYPT, DBG_log("decrypting %u bytes using algorithm %s",
-	    (unsigned) pbs_left(&md->message_pbs),
-	    enum_show(&oakley_enc_names, st->st_oakley.encrypt)));
+	DBG(DBG_CRYPT,
+	    DBG_log("decrypting %u bytes using algorithm %s"
+		, (unsigned) pbs_left(&md->message_pbs)
+		, enum_show(&oakley_enc_names, st->st_oakley.encrypt)));
 
 	/* do the specified decryption
 	 *
@@ -1403,7 +1427,7 @@
 	    if (pbs_left(&md->message_pbs) % e->blocksize != 0)
 	    {
 		loglog(RC_LOG_SERIOUS, "malformed message: not a multiple of encryption blocksize");
-		/* XXX Could send notification back */
+		SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		return;
 	    }
 
@@ -1425,8 +1449,8 @@
 		, st);
 	}
 
-	DBG_cond_dump(DBG_CRYPT, "decrypted:\n", md->message_pbs.cur,
-	    md->message_pbs.roof - md->message_pbs.cur);
+	DBG_cond_dump(DBG_CRYPT, "decrypted:\n", md->message_pbs.cur
+	    , md->message_pbs.roof - md->message_pbs.cur);
 
 	DBG_cond_dump(DBG_CRYPT, "next IV:"
 	    , st->st_new_iv, st->st_new_iv_len);
@@ -1438,7 +1462,7 @@
 	if (smc->flags & SMF_INPUT_ENCRYPTED)
 	{
 	    loglog(RC_LOG_SERIOUS, "packet rejected: should have been encrypted");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_FLAGS);
 	    return;
 	}
     }
@@ -1465,6 +1489,7 @@
 	    if (pd == &md->digest[PAYLIMIT])
 	    {
 		loglog(RC_LOG_SERIOUS, "more than %d payloads in message; ignored", PAYLIMIT);
+		SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		return;
 	    }
 
@@ -1481,6 +1506,7 @@
 		    loglog(RC_LOG_SERIOUS, "%smessage ignored because it contains an unknown or"
 			" unexpected payload type (%s) at the outermost level"
 			, excuse, enum_show(&payload_names, np));
+		    SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE);
 		    return;
 		}
 	    }
@@ -1494,6 +1520,7 @@
 		    loglog(RC_LOG_SERIOUS, "%smessage ignored because it contains a"
 			" payload type (%s) unexpected in this message"
 			, excuse, enum_show(&payload_names, np));
+		    SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE);
 		    return;
 		}
 		needed &= ~s;
@@ -1502,6 +1529,7 @@
 	    if (!in_struct(&pd->payload, sd, &md->message_pbs, &pd->pbs))
 	    {
 		loglog(RC_LOG_SERIOUS, "%smalformed payload in packet", excuse);
+		SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		return;
 	    }
 
@@ -1541,6 +1569,7 @@
 	    loglog(RC_LOG_SERIOUS, "message for %s is missing payloads %s"
 		, enum_show(&state_names, from_state)
 		, bitnamesof(payload_name, needed));
+	    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	    return;
 	}
     }
@@ -1556,6 +1585,7 @@
 	&& md->hdr.isa_np != ISAKMP_NEXT_SA)
 	{
 	    loglog(RC_LOG_SERIOUS, "malformed Phase 1 message: does not start with an SA payload");
+	    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	    return;
 	}
     }
@@ -1579,6 +1609,7 @@
 	if (md->hdr.isa_np != ISAKMP_NEXT_HASH)
 	{
 	    loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: does not start with a HASH payload");
+	    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	    return;
 	}
 
@@ -1592,6 +1623,7 @@
 		if (p != &md->digest[i])
 		{
 		    loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: SA payload is in wrong position");
+		    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		    return;
 		}
 	    }
@@ -1612,12 +1644,14 @@
 		    loglog(RC_LOG_SERIOUS, "malformed Quick Mode message:"
 			" if any ID payload is present,"
 			" there must be exactly two");
+		    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		    return;
 		}
 		if (id+1 != id->next)
 		{
 		    loglog(RC_LOG_SERIOUS, "malformed Quick Mode message:"
 			" the ID payloads are not adjacent");
+		    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		    return;
 		}
 	    }
@@ -1642,7 +1676,7 @@
 
 	for (p = md->chain[ISAKMP_NEXT_D]; p != NULL; p = p->next)
 	{
-	    loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload");
+	    accept_delete(st, md, p);
 	    DBG_cond_dump(DBG_PARSING, "del:", p->pbs.cur, pbs_left(&p->pbs));
 	}
 
@@ -1685,6 +1719,7 @@
 	    , smc->first_out_payload);
 
     complete_state_transition(mdp, smc->processor(md));
+#   undef SEND_NOTIFICATION
 }
 
 /* complete job started by the state-specific state transition function */
@@ -1910,8 +1945,8 @@
 		, enum_name(&state_names, st->st_state));
 
 	    DBG(DBG_CONTROL,
-		DBG_log("state transition function for %s had internal error",
-		    enum_name(&state_names, from_state)));
+		DBG_log("state transition function for %s had internal error"
+		    , enum_name(&state_names, from_state)));
 	    break;
 
 #ifdef DODGE_DH_MISSING_ZERO_BUG
@@ -1934,13 +1969,17 @@
 	    result = STF_FAIL;
 	    /* FALL THROUGH ... */
 	case STF_FAIL:
-	    /* XXX Could send notification back
-	     * As it is, we act as if this message never happened:
+	    /* As it is, we act as if this message never happened:
 	     * whatever retrying was in place, remains in place.
 	     */
 	    whack_log(RC_NOTIFICATION + md->note
 		, "%s: %s", enum_name(&state_names, st->st_state)
 		, enum_name(&ipsec_notification_names, md->note));
+
+	    if (st == NULL)
+		send_notification_from_md(md, md->note);
+	    else
+		send_notification_from_state(st, from_state, md->note);
 
 	    DBG(DBG_CONTROL,
 		DBG_log("state transition function for %s failed: %s"
Index: ipsec_doi.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/ipsec_doi.c,v
retrieving revision 1.181
diff -u -r1.181 ipsec_doi.c
--- ipsec_doi.c	9 Feb 2003 20:02:09 -0000	1.181
+++ ipsec_doi.c	13 Feb 2003 07:11:25 -0000
@@ -228,7 +228,6 @@
     {
 	loglog(RC_LOG_SERIOUS, "KE has %u byte DH public value; %u required"
 	    , (unsigned) pbs_left(pbs), (unsigned) gr->bytes);
-	/* XXX Could send notification back */
 #ifdef DODGE_DH_MISSING_ZERO_BUG
 	if (pbs_left(pbs) > gr->bytes)
 #endif
@@ -286,80 +285,210 @@
     return out_generic_chunk(np, &isakmp_nonce_desc, outs, *n, name);
 }
 
-/*
- * Send a notification to the peer. We could make a decision on
+/* Send a notification to the peer. We could make a decision on
  * whether to send the notification, based on the type and the
  * destination, if we care to.
- * XXX It doesn't handle DELETE notifications (which are also
- * XXX informational exchanges).
- * XXX Not modified to support ip_address and related (IPv4+IPv6) functions.
- */
-#if 0 /* not currently used */
-//static void
-//send_notification(int sock,
-//    u_int16_t type,
-//    u_char *spi,
-//    u_char spilen,
-//    u_char protoid,
-//    u_char *icookie,
-//    u_char *rcookie,
-//    msgid_t /*network order*/ msgid,
-//    struct sockaddr sa)
-//{
-//    u_char buffer[sizeof(struct isakmp_hdr) +
-//		 sizeof(struct isakmp_notification)];
-//    struct isakmp_hdr *isa = (struct isakmp_hdr *) buffer;
-//    struct isakmp_notification *isan = (struct isakmp_notification *)
-//				       (buffer + sizeof(struct isakmp_hdr));
-//
-//    memset(buffer, '\0', sizeof(struct isakmp_hdr) +
-//	  sizeof(struct isakmp_notification));
-//
-//    if (icookie != (u_char *) NULL)
-//	memcpy(isa->isa_icookie, icookie, COOKIE_SIZE);
-//
-//    if (rcookie != (u_char *) NULL)
-//	memcpy(isa->isa_rcookie, rcookie, COOKIE_SIZE);
-//
-//    /* Standard header */
-//    isa->isa_np = ISAKMP_NEXT_N;
-//    isa->isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
-//    isa->isa_xchg = ISAKMP_XCHG_INFO;
-//    isa->isa_msgid = msgid;
-//    isa->isa_length = htonl(sizeof(struct isakmp_hdr) +
-//			    sizeof(struct isakmp_notification) +
-//			    spilen);
-//
-//    /* Notification header */
-//    isan->isan_type = htons(type);
-//    isan->isan_doi = htonl(ISAKMP_DOI_IPSEC);
-//    isan->isan_length = htons(sizeof(struct isakmp_notification) + spilen);
-//    isan->isan_spisize = spilen;
-//    memcpy((u_char *)isan + sizeof(struct isakmp_notification), spi, spilen);
-//    isan->isan_protoid = protoid;
-//
-//    DBG(DBG_CONTROL, DBG_log("sending INFO type %s to %s",
-//	enum_show(&notification_names, type),
-//	show_sa(&sa)));
-//
-//    if (sendto(sock, buffer, ntohl(isa->isa_length), 0, &sa,
-//	       sizeof(sa)) != ntohl(isa->isa_length))
-//	log_errno((e, "sendto() failed in send_notification() to %s",
-//	    show_sa(&sa)));
-//    else
-//    {
-//	DBG(DBG_CONTROL, DBG_log("transmitted %d bytes", ntohl(isa->isa_length)));
-//    }
-//}
-#endif /* not currently used */
+ * This only handles notifications arising during negotiation.
+ * It doesn't handle DELETE notifications (see send_ipsec_delete)
+ */
+static void
+send_notification(struct state *p1st, notification_t type)
+{
+    u_char buffer[1024];
+    pb_stream pbs, r_hdr_pbs;
+    u_char *r_hashval, *r_hash_start;
+    bool secure = IS_ISAKMP_SA_ESTABLISHED(p1st->st_state);
+    msgid_t msgid = secure? generate_msgid(p1st) : MAINMODE_MSGID;
+
+    passert(IS_PHASE1(p1st->st_state));
+
+    log("sending %snotification %s to %s:%u"
+	, secure? "encrypted " : ""
+	, enum_name(&ipsec_notification_names, type)
+	, ip_str(&p1st->st_connection->that.host_addr)
+	, (unsigned)p1st->st_connection->that.host_port);
+
+    zero(&buffer);  /* paranoia */
+    init_pbs(&pbs, buffer, sizeof(buffer), "notification msg");
+
+    /* HDR* */
+    {
+	struct isakmp_hdr hdr;
+
+	hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
+	hdr.isa_np = secure ? ISAKMP_NEXT_HASH : ISAKMP_NEXT_N;
+	hdr.isa_xchg = ISAKMP_XCHG_INFO;
+	hdr.isa_msgid = msgid;
+	hdr.isa_flags = secure ? ISAKMP_FLAG_ENCRYPTION : 0;
+	memcpy(hdr.isa_icookie, p1st->st_icookie, COOKIE_SIZE);
+	memcpy(hdr.isa_rcookie, p1st->st_rcookie, COOKIE_SIZE);
+	if (!out_struct(&hdr, &isakmp_hdr_desc, &pbs, &r_hdr_pbs))
+	    impossible();
+    }
+
+    /* HASH -- value to be filled later */
+    if (secure)
+    {
+	pb_stream hash_pbs;
+	if (!out_generic(ISAKMP_NEXT_N, &isakmp_hash_desc, &r_hdr_pbs
+	, &hash_pbs))
+	    impossible();
+	r_hashval = hash_pbs.cur;  /* remember where to plant value */
+	if (!out_zero(
+#ifdef _IKE_ALG_H
+	    p1st->st_oakley.hasher->hash_digest_size
+#else
+	    p1st->st_oakley.hasher->hash_digest_len
+#endif
+	    , &hash_pbs, "HASH(1)"))
+	{
+	    impossible();
+	}
+	close_output_pbs(&hash_pbs);
+	r_hash_start = r_hdr_pbs.cur; /* hash from after HASH(1) */
+    }
+
+    /* Notification Payload */
+    {
+	pb_stream not_pbs;
+	struct isakmp_notification isan;
+
+	isan.isan_doi = ISAKMP_DOI_IPSEC;
+	isan.isan_np = ISAKMP_NEXT_NONE;
+	isan.isan_type = type;
+	isan.isan_spisize = 0;
+	isan.isan_protoid = PROTO_ISAKMP;
+
+	if (!out_struct(&isan, &isakmp_notification_desc, &r_hdr_pbs, &not_pbs))
+	    impossible();
+	close_output_pbs(&not_pbs);
+    }
+
+    /* calculate hash value and patch into Hash Payload */
+    if (secure)
+    {
+	struct hmac_ctx ctx;
+
+	hmac_init_chunk(&ctx, p1st->st_oakley.hasher, p1st->st_skeyid_a);
+	hmac_update(&ctx, (u_char *) &msgid, sizeof(msgid_t));
+	hmac_update(&ctx, r_hash_start, r_hdr_pbs.cur-r_hash_start);
+	hmac_final(r_hashval, &ctx);
+
+#ifdef _IKE_ALG_H
+	DBG(DBG_CRYPT,
+	    DBG_log("HASH(1) computed:");
+	    DBG_dump("", r_hashval, ctx.hmac_digest_size);
+	)
+#else
+	DBG(DBG_CRYPT,
+	    DBG_log("HASH(1) computed:");
+	    DBG_dump("", r_hashval, ctx.hmac_digest_len);
+	)
+#endif
+    }
+
+    /* Encrypt message (preserve st_iv) */
+    if (secure)
+    {
+	u_char old_iv[MAX_DIGEST_LEN];
+
+	if (p1st->st_iv_len > MAX_DIGEST_LEN)
+	    impossible();
+	memcpy(old_iv, p1st->st_iv, p1st->st_iv_len);
+	init_phase2_iv(p1st, &msgid);
+	if (!encrypt_message(&r_hdr_pbs, p1st))
+	    impossible();
+	memcpy(p1st->st_iv, old_iv, p1st->st_iv_len);
+    }
+    else
+    {
+	close_output_pbs(&r_hdr_pbs);
+    }
+
+    /* Send packet (preserve st_tpacket) */
+    {
+	chunk_t saved_tpacket = p1st->st_tpacket;
+
+	setchunk(p1st->st_tpacket, pbs.start, pbs_offset(&pbs));
+	send_packet(p1st, "notification packet");
+	p1st->st_tpacket = saved_tpacket;
+    }
+}
+
+void
+send_notification_from_state(struct state *st
+, enum state_kind state
+, notification_t type)
+{
+    struct state *p1st = st;
+
+    if (state == STATE_UNDEFINED)
+	state = st->st_state;
+
+    if (IS_QUICK(state))
+    {
+	/* find a Main Mode state with our cookies */
+	p1st = find_state(st->st_icookie, st->st_rcookie
+	    , &st->st_connection->that.host_addr, MAINMODE_MSGID);
+
+	if (p1st == NULL)
+	{
+	    /* Since we are in a negotiation, this ought to be rare.
+	     * Should happen only if the negotiation drags on a long time.
+	     */
+	    loglog(RC_LOG_SERIOUS
+		, "no Phase 1 state for Quick Mode notification");
+	    return;
+	}
+    }
+
+    send_notification(p1st, type);
+}
+
+void
+send_notification_from_md(struct msg_digest *md, notification_t type)
+{
+    if (md->st == NULL)
+    {
+	/**
+	 * Create a dummy state to be able to use send_packet in
+	 * send_notification
+	 *
+	 * we need to set:
+	 *   st_state
+	 *   st_connection->that.host_addr
+	 *   st_connection->that.host_port
+	 *   st_connection->interface
+	 *   st_icookie
+	 *   st_rcookie
+	 */
+	struct state st;
+	struct connection cnx;
 
-/* Send a Delete Notification to announce deletion of inbound IPSEC SAs.
+	zero(&st);
+	zero(&cnx);
+	st.st_state = STATE_MAIN_R0;	/* lie! */
+	st.st_connection = &cnx;
+	cnx.that.host_addr = md->sender;
+	cnx.that.host_port = md->sender_port;
+	cnx.interface = md->iface;
+	memcpy(st.st_icookie, md->hdr.isa_icookie, COOKIE_SIZE);
+	memcpy(st.st_rcookie, md->hdr.isa_rcookie, COOKIE_SIZE);
+
+	send_notification(&st, type);
+    }
+    else
+    {
+	send_notification_from_state(md->st, STATE_UNDEFINED, type);
+    }
+}
+
+/* Send a Delete Notification to announce deletion of inbound IPSEC/ISAKMP SAs.
  * Ignores states that don't have any.
- * Delete Notifications cannot announce deletion of outbound IPSEC SAs.
- * We don't bother announcing deletion of ISAKMP SAs at this point.
+ * Delete Notifications cannot announce deletion of outbound IPSEC/ISAKMP SAs.
  */
 void
-send_ipsec_delete(struct state *p2st)
+send_delete(struct state *st)
 {
     pb_stream reply_pbs;
     pb_stream r_hdr_pbs;
@@ -371,49 +500,45 @@
     u_char
 	*r_hashval,	/* where in reply to jam hash value */
 	*r_hash_start;	/* start of what is to be hashed */
+    bool isakmp_sa = FALSE;
 
-    if (!IS_IPSEC_SA_ESTABLISHED(p2st->st_state))
-	return;	/* nothing to do */
-
-    /* find an ISAKMP SA that we can use -- must be established */
-    p1st = find_phase1_state(p2st->st_connection, ISAKMP_SA_ESTABLISHED_STATES);
-    if (p1st == NULL)
+    if (IS_IPSEC_SA_ESTABLISHED(st->st_state))
     {
-	DBG(DBG_CONTROL, DBG_log("no Phase 1 state for Delete"));
-	return;
-    }
+	p1st = find_phase1_state(st->st_connection, ISAKMP_SA_ESTABLISHED_STATES);
+	if (p1st == NULL)
+	{
+	    DBG(DBG_CONTROL, DBG_log("no Phase 1 state for Delete"));
+	    return;
+	}
 
-    msgid = generate_msgid(p1st);
+	if (st->st_ah.present)
+	{
+	    ns->spi = st->st_ah.our_spi;
+	    ns->dst = st->st_connection->this.host_addr;
+	    ns->proto = PROTO_IPSEC_AH;
+	    ns++;
+	}
+	if (st->st_esp.present)
+	{
+	    ns->spi = st->st_esp.our_spi;
+	    ns->dst = st->st_connection->this.host_addr;
+	    ns->proto = PROTO_IPSEC_ESP;
+	    ns++;
+	}
 
-    if (p2st->st_ah.present)
+	passert(ns != said);    /* there must be some SAs to delete */
+    }
+    else if (IS_ISAKMP_SA_ESTABLISHED(st->st_state))
     {
-	ns->spi = p2st->st_ah.attrs.spi;
-	ns->dst = p2st->st_connection->this.host_addr;
-	ns->proto = PROTO_IPSEC_AH;
-	ns++;
-    }
-    if (p2st->st_esp.present)
-    {
-	ns->spi = p2st->st_esp.attrs.spi;
-	ns->dst = p2st->st_connection->this.host_addr;
-	ns->proto = PROTO_IPSEC_ESP;
-	ns++;
-    }
-    /* I doubt that it makes sense to delete an IPCOMP with a well-known CPI.
-     * Maybe it never makes sense to delete a CPI.
-     */
-#if 0
-    if (p2st->st_ipcomp.present)
-    {
-	ns->spi = p2st->st_ipcomp.attrs.spi;
-	ns->dst = p2st->st_connection->this.host_addr;
-	ns->proto = PROTO_IPCOMP;
-	ns++;
+	p1st = st;
+	isakmp_sa = TRUE;
+    }
+    else
+    {
+	return; /* nothing to do */
     }
-#endif
-    /* IPIP isn't a real SA, so we don't mention it */
 
-    passert(ns != said);	/* there must be some SAs to delete */
+    msgid = generate_msgid(p1st);
 
     zero(buffer);
     init_pbs(&reply_pbs, buffer, sizeof(buffer), "delete msg");
@@ -447,21 +572,46 @@
     }
 
     /* Delete Payloads */
-    while (ns != said) {
-	pb_stream del_pbs;
-	struct isakmp_delete isad;
-
-	ns--;
-	isad.isad_doi = ISAKMP_DOI_IPSEC;
-	isad.isad_np = ns == said? ISAKMP_NEXT_NONE : ISAKMP_NEXT_D;
-	isad.isad_spisize = sizeof(ipsec_spi_t);
-	isad.isad_protoid = ns->proto;
-
-	isad.isad_nospi = 1;
-	if (!out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs, &del_pbs)
-	|| !out_raw(&ns->spi, sizeof(ipsec_spi_t), &del_pbs, "delete payload"))
-	    impossible();
-	close_output_pbs(&del_pbs);
+    if (isakmp_sa)
+    {
+       pb_stream del_pbs;
+       struct isakmp_delete isad;
+       u_char isakmp_spi[2*COOKIE_SIZE];
+
+       isad.isad_doi = ISAKMP_DOI_IPSEC;
+       isad.isad_np = ISAKMP_NEXT_NONE;
+       isad.isad_spisize = (2 * COOKIE_SIZE);
+       isad.isad_protoid = PROTO_ISAKMP;
+       isad.isad_nospi = 1;
+
+       memcpy(isakmp_spi, st->st_icookie, COOKIE_SIZE);
+       memcpy(isakmp_spi+COOKIE_SIZE, st->st_rcookie, COOKIE_SIZE);
+
+       if (!out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs, &del_pbs)
+       || !out_raw(&isakmp_spi, (2*COOKIE_SIZE), &del_pbs, "delete payload"))
+           impossible();
+       close_output_pbs(&del_pbs);
+    }
+    else
+    {
+	while (ns != said)
+	{
+
+	    pb_stream del_pbs;
+	    struct isakmp_delete isad;
+
+	    ns--;
+	    isad.isad_doi = ISAKMP_DOI_IPSEC;
+	    isad.isad_np = ns == said? ISAKMP_NEXT_NONE : ISAKMP_NEXT_D;
+	    isad.isad_spisize = sizeof(ipsec_spi_t);
+	    isad.isad_protoid = ns->proto;
+
+	    isad.isad_nospi = 1;
+	    if (!out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs, &del_pbs)
+	    || !out_raw(&ns->spi, sizeof(ipsec_spi_t), &del_pbs, "delete payload"))
+		impossible();
+	    close_output_pbs(&del_pbs);
+	}
     }
 
     /* calculate hash value and patch into Hash Payload */
@@ -504,6 +654,157 @@
     }
 }
 
+void
+accept_delete(struct state *st, struct msg_digest *md, struct payload_digest *p)
+{
+    struct isakmp_delete *d = &(p->payload.delete);
+    size_t sizespi;
+    int i;
+
+    if (st == NULL && (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION) == 0)
+    {
+	loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: not encrypted");
+	return;
+    }
+
+    if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
+    {
+	/* can't happen (if msg is encrypt), but just to be sure */
+	loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: "
+	"ISAKMP SA not established");
+	return;
+    }
+
+    if (d->isad_nospi == 0)
+    {
+	loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: no SPI");
+	return;
+    }
+
+    switch (d->isad_protoid)
+    {
+    case PROTO_ISAKMP:
+	sizespi = 2 * COOKIE_SIZE;
+	break;
+    case PROTO_IPSEC_AH:
+    case PROTO_IPSEC_ESP:
+	sizespi = sizeof(ipsec_spi_t);
+	break;
+    case PROTO_IPCOMP:
+	/* nothing interesting to delete */
+	return;
+    default:
+	loglog(RC_LOG_SERIOUS
+	    , "ignoring Delete SA payload: unknown Protocol ID (%s)"
+	    , enum_show(&protocol_names, d->isad_protoid));
+	return;
+    }
+
+    if (d->isad_spisize != sizespi)
+    {
+	loglog(RC_LOG_SERIOUS
+	    , "ignoring Delete SA payload: bad SPI size (%d) for %s"
+	    , d->isad_spisize, enum_show(&protocol_names, d->isad_protoid));
+	return;
+    }
+
+    if (pbs_left(&p->pbs) != d->isad_nospi * sizespi)
+    {
+	loglog(RC_LOG_SERIOUS
+	    , "ignoring Delete SA payload: invalid payload size");
+	return;
+    }
+
+    for (i = 0; i < d->isad_nospi; i++)
+    {
+	u_char *spi = p->pbs.cur + (i * sizespi);
+
+	if (d->isad_protoid == PROTO_ISAKMP)
+	{
+	    /**
+	     * ISAKMP
+	     */
+	    struct state *dst = find_state(spi /*iCookie*/
+		, spi+COOKIE_SIZE /*rCookie*/
+		, &st->st_connection->that.host_addr
+		, MAINMODE_MSGID);
+
+	    if (dst == NULL)
+	    {
+		loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: "
+		    "ISAKMP SA not found (maybe expired)");
+	    }
+	    else
+	    {
+		loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+		    "deleting ISAKMP State #%lu", dst->st_serialno);
+		delete_state(dst);
+	    }
+	}
+	else
+	{
+	    /**
+	     * IPSEC (ESP/AH)
+	     */
+	    bool bogus;
+	    struct state *dst = find_phase2_state_to_delete(st
+		, d->isad_protoid
+		, *(ipsec_spi_t *)spi	/* network order */
+		, &bogus);
+
+	    if (dst == NULL)
+	    {
+		loglog(RC_LOG_SERIOUS
+		    , "ignoring Delete SA payload: %s SA not found (%s)"
+		    , enum_show(&protocol_names, d->isad_protoid)
+		    , bogus ? "our SPI - bogus implementation" : "maybe expired");
+	    }
+	    else
+	    {
+		struct connection *rc = dst->st_connection;
+
+		if (rc != NULL
+		&& rc->newest_ipsec_sa == dst->st_serialno
+		&& (rc->policy & POLICY_UP))
+		    {
+		    /* Last IPSec SA for a permanent connection that we
+		     * have initiated.  Replace it in a few seconds.
+		     *
+		     * Useful if the other peer is rebooting.
+		     */
+#define DELETE_SA_DELAY  EVENT_RETRANSMIT_DELAY_0
+		    if (dst->st_event != NULL
+		    && dst->st_event->ev_type == EVENT_SA_REPLACE
+		    && dst->st_event->ev_time <= DELETE_SA_DELAY + now())
+		    {
+			/* Patch from Angus Lees to ignore retransmited
+			 * Delete SA.
+			 */
+			loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+			    "already replacing IPSEC State #%lu in %d seconds"
+			    , dst->st_serialno, (int)(dst->st_event->ev_time - now()));
+		    }
+		    else
+		    {
+			loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+			    "replace IPSEC State #%lu in %d seconds"
+			    , dst->st_serialno, DELETE_SA_DELAY);
+			dst->st_margin = DELETE_SA_DELAY;
+			delete_event(dst);
+			event_schedule(EVENT_SA_REPLACE, DELETE_SA_DELAY, dst);
+		    }
+		}
+		else
+		{
+		    loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+			"deleting IPSEC State #%lu", dst->st_serialno);
+		    delete_state(dst);
+		}
+	    }
+	}
+    }
+}
+
 /* The whole message must be a multiple of 4 octets.
  * I'm not sure where this is spelled out, but look at
  * rfc2408 3.6 Transform Payload.
@@ -826,8 +1127,8 @@
 	    /* XXX */
 
 	default:
-	    exit_log("generate_skeyids_iv(): unsupported authentication method %s",
-		enum_show(&oakley_auth_names, st->st_oakley.auth));
+	    exit_log("generate_skeyids_iv(): unsupported authentication method %s"
+		, enum_show(&oakley_auth_names, st->st_oakley.auth));
     }
 
     /* generate SKEYID_* from SKEYID */
@@ -1355,7 +1656,6 @@
 	if (s.best_ugh[0] == '9')
 	{
 	    loglog(RC_LOG_SERIOUS, "%s", s.best_ugh + 1);
-	    /* XXX Could send notification back */
 	    return STF_FAIL + INVALID_HASH_INFORMATION;
 	}
 	else
@@ -1402,7 +1702,6 @@
 	{ \
 	    DBG_cond_dump(DBG_CRYPT, "received " hash_name ":", hash_pbs->cur, pbs_left(hash_pbs)); \
 	    loglog(RC_LOG_SERIOUS, "received " hash_name " does not match computed value in " msg_name); \
-	    /* XXX Could send notification back */ \
 	    return STF_FAIL + INVALID_HASH_INFORMATION; \
 	} \
     }
@@ -1805,7 +2104,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "improper %s identification payload: %s"
 		    , enum_show(&ident_names, peer.kind), ugh);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	}
@@ -1832,7 +2130,6 @@
 	break;
 
     default:
-	/* XXX Could send notification back */
 	loglog(RC_LOG_SERIOUS, "Unacceptable identity type (%s) in Phase 1 ID Payload"
 	    , enum_show(&ident_names, peer.kind));
 	return FALSE;
@@ -1843,9 +2140,8 @@
 	    char buf[IDTOA_BUF];
 
 	    idtoa(&peer, buf, sizeof(buf));
-	    DBG_log("Peer's ID is %s: '%s'",
-		enum_show(&ident_names, id->isaid_idtype),
-		buf);
+	    DBG_log("Peer's ID is %s: '%s'"
+		, enum_show(&ident_names, id->isaid_idtype), buf);
 	});
 
     /* Now that we've decoded the ID payload, let's see if we
@@ -1939,7 +2235,6 @@
 	    /* XXX support more */
 	    loglog(RC_LOG_SERIOUS, "unsupported ID type %s"
 		, idtypename);
-	    /* XXX Could send notification back */
 	    return FALSE;
     }
 
@@ -1975,7 +2270,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s has wrong length in Quick I1 (%s)"
 		    , which, idtypename, ugh);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	    if (isanyaddr(&temp_address))
@@ -2001,7 +2295,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1"
 		    , which, idtypename);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	    ugh = initaddr(id_pbs->cur
@@ -2018,7 +2311,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s bad subnet in Quick I1 (%s)"
 		    , which, idtypename, ugh);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	    DBG(DBG_PARSING | DBG_CONTROL,
@@ -2041,7 +2333,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1"
 		    , which, idtypename);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	    ugh = initaddr(id_pbs->cur, afi->ia_sz, afi->af, &temp_address_from);
@@ -2052,7 +2343,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s malformed (%s) in Quick I1"
 		    , which, idtypename, ugh);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 
@@ -2139,8 +2429,8 @@
 		needed_len = DES_CBC_BLOCK_SIZE * 3;
 		break;
 	    default:
-		exit_log("transform %s not implemented yet",
-		    enum_show(&esp_transformid_names, pi->attrs.transid));
+		exit_log("transform %s not implemented yet"
+		    , enum_show(&esp_transformid_names, pi->attrs.transid));
 	    }
 
 	    switch (pi->attrs.auth)
@@ -2155,8 +2445,8 @@
 		break;
 	    case AUTH_ALGORITHM_DES_MAC:
 	    default:
-		exit_log("AUTH algorithm %s not implemented yet",
-		    enum_show(&auth_alg_names, pi->attrs.auth));
+		exit_log("AUTH algorithm %s not implemented yet"
+		    , enum_show(&auth_alg_names, pi->attrs.auth));
 	    }
 	    break;
 
@@ -2170,14 +2460,14 @@
 		needed_len = HMAC_SHA1_KEY_LEN;
 		break;
 	    default:
-		exit_log("transform %s not implemented yet",
-		    enum_show(&ah_transformid_names, pi->attrs.transid));
+		exit_log("transform %s not implemented yet"
+		    , enum_show(&ah_transformid_names, pi->attrs.transid));
 	    }
 	    break;
 
     default:
-	exit_log("protocol %s not implemented yet",
-	    enum_show(&protocol_names, protoid));
+	exit_log("protocol %s not implemented yet"
+	    , enum_show(&protocol_names, protoid));
 	break;
     }
 
@@ -2232,10 +2522,10 @@
 	    hmac_reinit(&ctx_me);
 	    hmac_reinit(&ctx_peer);
 
-	    hmac_update(&ctx_me, pi->our_keymat + i - ctx_me.hmac_digest_len,
-		ctx_me.hmac_digest_len);
-	    hmac_update(&ctx_peer, pi->peer_keymat + i - ctx_peer.hmac_digest_len,
-		ctx_peer.hmac_digest_len);
+	    hmac_update(&ctx_me, pi->our_keymat + i - ctx_me.hmac_digest_len
+		, ctx_me.hmac_digest_len);
+	    hmac_update(&ctx_peer, pi->peer_keymat + i - ctx_peer.hmac_digest_len
+		, ctx_peer.hmac_digest_len);
 	}
     }
 
@@ -2719,7 +3009,6 @@
 		DBG_cond_dump(DBG_CRYPT, "received HASH:"
 		    , hash_pbs->cur, pbs_left(hash_pbs));
 		loglog(RC_LOG_SERIOUS, "received Hash Payload does not match computed value");
-		/* XXX Could send notification back */
 		r = STF_FAIL + INVALID_HASH_INFORMATION;
 	    }
 	}
Index: ipsec_doi.h
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/ipsec_doi.h,v
retrieving revision 1.29
diff -u -r1.29 ipsec_doi.h
--- ipsec_doi.h	28 Jul 2002 03:56:25 -0000	1.29
+++ ipsec_doi.h	13 Feb 2003 07:11:25 -0000
@@ -41,6 +41,13 @@
     quick_inR1_outI2,
     quick_inI2;
 
-extern void send_ipsec_delete(struct state *p2st);
+extern void send_delete(struct state *st);
+extern void accept_delete(struct state *st, struct msg_digest *md,
+     struct payload_digest *p);
+
+extern void send_notification_from_state(struct state *st,
+     enum state_kind state, notification_t type);
+extern void send_notification_from_md(struct msg_digest *md, notification_t type);
+
 
 extern const char *init_pluto_vendorid(void);
Index: spdb.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/spdb.c,v
retrieving revision 1.81
diff -u -r1.81 spdb.c
--- spdb.c	7 Feb 2003 04:25:35 -0000	1.81
+++ spdb.c	13 Feb 2003 07:11:26 -0000
@@ -679,7 +679,6 @@
     if (sa->isasa_doi != ISAKMP_DOI_IPSEC)
     {
 	loglog(RC_LOG_SERIOUS, "Unknown/unsupported DOI %s", enum_show(&doi_names, sa->isasa_doi));
-	/* XXX Could send notification back */
 	return DOI_NOT_SUPPORTED;
     }
 
@@ -693,7 +692,6 @@
     {
 	loglog(RC_LOG_SERIOUS, "unsupported IPsec DOI situation (%s)"
 	    , bitnamesof(sit_bit_names, ipsecdoisit));
-	/* XXX Could send notification back */
 	return SITUATION_NOT_SUPPORTED;
     }
 
@@ -1485,7 +1483,6 @@
     if (sa->isasa_doi != ISAKMP_DOI_IPSEC)
     {
 	loglog(RC_LOG_SERIOUS, "Unknown or unsupported DOI %s", enum_show(&doi_names, sa->isasa_doi));
-	/* XXX Could send notification back */
 	return DOI_NOT_SUPPORTED;
     }
 
@@ -1497,7 +1494,6 @@
     {
 	loglog(RC_LOG_SERIOUS, "unsupported IPsec DOI situation (%s)"
 	    , bitnamesof(sit_bit_names, ipsecdoisit));
-	/* XXX Could send notification back */
 	return SITUATION_NOT_SUPPORTED;
     }
 
Index: state.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/state.c,v
retrieving revision 1.102
diff -u -r1.102 state.c
--- state.c	9 Nov 2002 23:19:02 -0000	1.102
+++ state.c	13 Feb 2003 07:11:26 -0000
@@ -293,8 +293,9 @@
     }
 
     /* tell the other side of any IPSEC SAs that are going down */
-    if (IS_IPSEC_SA_ESTABLISHED(st->st_state))
-	send_ipsec_delete(st);
+    if (IS_IPSEC_SA_ESTABLISHED(st->st_state)
+    || IS_ISAKMP_SA_ESTABLISHED(st->st_state))
+	send_delete(st);
 
     delete_event(st);	/* delete any pending timer event */
 
@@ -489,8 +490,10 @@
  * Find a state object.
  */
 struct state *
-find_state(const u_char *icookie, const u_char *rcookie,
-		const ip_address *peer, msgid_t /*network order*/  msgid)
+find_state(const u_char *icookie
+, const u_char *rcookie
+, const ip_address *peer
+, msgid_t /*network order*/  msgid)
 {
     struct state *st = *state_hash(icookie, rcookie, peer);
 
@@ -531,6 +534,40 @@
 		&& memcmp(st->st_tpacket.ptr, packet, packet_len) == 0)
 		    return st;
 
+    return NULL;
+}
+
+struct state *
+find_phase2_state_to_delete(const struct state *p1st
+, u_int8_t protoid
+, ipsec_spi_t spi
+, bool *bogus)
+{
+    struct state *st;
+    int i;
+
+    *bogus = FALSE;
+    for (i = 0; i < STATE_TABLE_SIZE; i++)
+    {
+	for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+	{
+	    if (IS_IPSEC_SA_ESTABLISHED(st->st_state)
+	    && p1st->st_connection->host_pair == st->st_connection->host_pair
+	    && same_peer_ids(p1st->st_connection, st->st_connection, NULL))
+	    {
+		struct ipsec_proto_info *pr = protoid == PROTO_IPSEC_AH
+		    ? &st->st_ah : &st->st_esp;
+
+		if (pr->present)
+		{
+		    if (pr->attrs.spi == spi)
+			return st;
+		    if (pr->our_spi)
+			*bogus = TRUE;
+		}
+	    }
+	}
+    }
     return NULL;
 }
 
Index: state.h
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/state.h,v
retrieving revision 1.61
diff -u -r1.61 state.h
--- state.h	9 Nov 2002 23:19:02 -0000	1.61
+++ state.h	13 Feb 2003 07:11:27 -0000
@@ -39,6 +39,7 @@
  */
 
 typedef u_int32_t msgid_t;	/* Network order! */
+#define MAINMODE_MSGID    ((msgid_t) 0)
 
 struct state;	/* forward declaration of tag */
 extern bool reserve_msgid(struct state *isakmp_sa, msgid_t msgid);
@@ -224,6 +225,8 @@
 	const ip_address *peer,
 	msgid_t msgid),
     *state_with_serialno(so_serial_t sn),
+    *find_phase2_state_to_delete(const struct state *p1st, u_int8_t protoid,
+	ipsec_spi_t spi, bool *bogus),
     *find_phase1_state(const struct connection *c, lset_t ok_states),
     *find_sender(size_t packet_len, u_char *packet);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.