isisd: handle lsp confusion (ISO/IEC 10589:2002 7.3.16.2)

Signed-off-by: Christian Franke <chris@opensourcerouting.org>
Acked-by: Donald Sharp <sharpd@cumulusnetworks.com>
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index d2be27b..bbf0d73 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -1309,6 +1309,7 @@
   u_char lspid[ISIS_SYS_ID_LEN + 2];
   struct isis_passwd *passwd;
   uint16_t pdu_len;
+  int lsp_confusion;
 
   if (isis->debugs & DEBUG_UPDATE_PACKETS)
     {
@@ -1483,6 +1484,21 @@
 
   /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented  FIXME: do it */
 
+  /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num but
+   *            wrong checksum, initiate a purge. */
+  if (lsp
+      && (lsp->lsp_header->seq_num == hdr->seq_num)
+      && (lsp->lsp_header->checksum != hdr->checksum))
+    {
+      zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.",
+                circuit->area->area_tag, rawlspid_print(hdr->lsp_id),
+                ntohl(hdr->seq_num));
+      hdr->rem_lifetime = 0;
+      lsp_confusion = 1;
+    }
+  else
+    lsp_confusion = 0;
+
   /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */
   if (hdr->rem_lifetime == 0)
     {
@@ -1505,14 +1521,20 @@
                   lsp_update (lsp, circuit->rcv_stream, circuit->area, level);
 		  /* ii */
                   lsp_set_all_srmflags (lsp);
-		  /* iii */
-		  ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
 		  /* v */
 		  ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags);	/* FIXME: OTHER than c */
-		  /* iv */
-		  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
-		    ISIS_SET_FLAG (lsp->SSNflags, circuit);
 
+		  /* For the case of lsp confusion, flood the purge back to its
+		   * originator so that it can react. Otherwise, don't reflood
+		   * through incoming circuit as usual */
+		  if (!lsp_confusion)
+		    {
+		      /* iii */
+		      ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
+		      /* iv */
+		      if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+		        ISIS_SET_FLAG (lsp->SSNflags, circuit);
+		    }
 		}		/* 7.3.16.4 b) 2) */
 	      else if (comp == LSP_EQUAL)
 		{