]> git.proxmox.com Git - mirror_frr.git/blobdiff - isisd/isis_pdu.c
isisd: implement the 'lsp-received' notification
[mirror_frr.git] / isisd / isis_pdu.c
index 5c4e3a35bcb44baf60bd4374df7ff909f4ea97af..6b3cd031923334399f0d28fed837c192cd0a1979 100644 (file)
@@ -56,6 +56,9 @@
 #include "isisd/isis_mt.h"
 #include "isisd/isis_tlvs.h"
 #include "isisd/isis_errors.h"
+#include "isisd/fabricd.h"
+#include "isisd/isis_tx_queue.h"
+#include "isisd/isis_pdu_counter.h"
 
 static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit,
                   int level)
@@ -86,12 +89,13 @@ static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit,
        /* Update PDU length */
        stream_putw_at(circuit->snd_stream, lenp, length);
 
+       pdu_counter_count(circuit->area->pdu_tx_counters, pdu_type);
        retval = circuit->tx(circuit, level);
        if (retval != ISIS_OK)
-               flog_err(ISIS_ERR_PACKET,
-                         "ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
-                         circuit->area->area_tag, level,
-                         circuit->interface->name);
+               flog_err(EC_ISIS_PACKET,
+                        "ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
+                        circuit->area->area_tag, level,
+                        circuit->interface->name);
 
        return retval;
 }
@@ -207,6 +211,12 @@ static int process_p2p_hello(struct iih_info *iih)
        thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time,
                         &adj->t_expire);
 
+       /* While fabricds initial sync is in progress, ignore hellos from other
+        * interfaces than the one we are performing the initial sync on. */
+       if (fabricd_initial_sync_is_in_progress(iih->circuit->area)
+           && fabricd_initial_sync_circuit(iih->circuit->area) != iih->circuit)
+               return ISIS_OK;
+
        /* 8.2.5.2 a) a match was detected */
        if (isis_tlvs_area_addresses_match(iih->tlvs,
                                           iih->circuit->area->area_addrs)) {
@@ -543,6 +553,10 @@ static int pdu_len_validate(uint16_t pdu_len, struct isis_circuit *circuit)
 static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
                         uint8_t *ssnpa)
 {
+       /* keep a copy of the raw pdu for NB notifications */
+       size_t pdu_start = stream_get_getp(circuit->rcv_stream);
+       size_t pdu_end = stream_get_endp(circuit->rcv_stream);
+       char raw_pdu[pdu_end - pdu_start];
        bool p2p_hello = (pdu_type == P2P_HELLO);
        int level = p2p_hello ? 0
                              : (pdu_type == L1_LAN_HELLO) ? ISIS_LEVEL1
@@ -552,6 +566,9 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
                        ? "P2P IIH"
                        : (level == ISIS_LEVEL1) ? "L1 LAN IIH" : "L2 LAN IIH";
 
+
+       stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
+                       pdu_end - pdu_start);
        if (isis->debugs & DEBUG_ADJ_PACKETS) {
                zlog_debug("ISIS-Adj (%s): Rcvd %s on %s, cirType %s, cirID %u",
                           circuit->area->area_tag, pdu_name,
@@ -566,11 +583,21 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
        if (p2p_hello) {
                if (circuit->circ_type != CIRCUIT_T_P2P) {
                        zlog_warn("p2p hello on non p2p circuit");
+#ifndef FABRICD
+                       isis_notif_reject_adjacency(
+                               circuit, "p2p hello on non p2p circuit",
+                               raw_pdu);
+#endif /* ifndef FABRICD */
                        return ISIS_WARNING;
                }
        } else {
                if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
                        zlog_warn("lan hello on non broadcast circuit");
+#ifndef FABRICD
+                       isis_notif_reject_adjacency(
+                               circuit, "lan hello on non broadcast circuit",
+                               raw_pdu);
+#endif /* ifndef FABRICD */
                        return ISIS_WARNING;
                }
 
@@ -578,6 +605,12 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
                        zlog_debug(
                                "level %d LAN Hello received over circuit with externalDomain = true",
                                level);
+#ifndef FABRICD
+                       isis_notif_reject_adjacency(
+                               circuit,
+                               "LAN Hello received over circuit with externalDomain = true",
+                               raw_pdu);
+#endif /* ifndef FABRICD */
                        return ISIS_WARNING;
                }
 
@@ -588,6 +621,10 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
                                        circuit->area->area_tag,
                                        circuit->interface->name);
                        }
+#ifndef FABRICD
+                       isis_notif_reject_adjacency(
+                               circuit, "Interface level mismatch", raw_pdu);
+#endif /* ifndef FABRICD */
                        return ISIS_WARNING;
                }
        }
@@ -613,13 +650,21 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
                        "ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %" PRIu16,
                        circuit->area->area_tag, pdu_name,
                        circuit->interface->name, iih.pdu_len);
+#ifndef FABRICD
+               isis_notif_reject_adjacency(circuit, "Invalid PDU length",
+                                           raw_pdu);
+#endif /* ifndef FABRICD */
                return ISIS_WARNING;
        }
 
        if (!p2p_hello && !(level & iih.circ_type)) {
-               flog_err(ISIS_ERR_PACKET,
-                         "Level %d LAN Hello with Circuit Type %d", level,
-                         iih.circ_type);
+               flog_err(EC_ISIS_PACKET,
+                        "Level %d LAN Hello with Circuit Type %d", level,
+                        iih.circ_type);
+#ifndef FABRICD
+               isis_notif_reject_adjacency(
+                       circuit, "LAN Hello with wrong IS-level", raw_pdu);
+#endif /* ifndef FABRICD */
                return ISIS_ERROR;
        }
 
@@ -629,24 +674,47 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
        if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream),
                             circuit->rcv_stream, &iih.tlvs, &error_log)) {
                zlog_warn("isis_unpack_tlvs() failed: %s", error_log);
+#ifndef FABRICD
+               isis_notif_reject_adjacency(circuit, "Failed to unpack TLVs",
+                                           raw_pdu);
+#endif /* ifndef FABRICD */
                goto out;
        }
 
        if (!iih.tlvs->area_addresses.count) {
                zlog_warn("No Area addresses TLV in %s", pdu_name);
+#ifndef FABRICD
+               /* send northbound notification */
+               isis_notif_area_mismatch(circuit, raw_pdu);
+#endif /* ifndef FABRICD */
                goto out;
        }
 
        if (!iih.tlvs->protocols_supported.count) {
                zlog_warn("No supported protocols TLV in %s", pdu_name);
+#ifndef FABRICD
+               isis_notif_reject_adjacency(
+                       circuit, "No supported protocols TLV", raw_pdu);
+#endif /* ifndef FABRICD */
                goto out;
        }
 
-       if (!isis_tlvs_auth_is_valid(iih.tlvs, &circuit->passwd,
-                                    circuit->rcv_stream, false)) {
+       int auth_code = isis_tlvs_auth_is_valid(iih.tlvs, &circuit->passwd,
+                                               circuit->rcv_stream, false);
+       if (auth_code != ISIS_AUTH_OK) {
                isis_event_auth_failure(circuit->area->area_tag,
                                        "IIH authentication failure",
                                        iih.sys_id);
+#ifndef FABRICD
+               /* send northbound notification */
+               stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
+                               pdu_end - pdu_start);
+               if (auth_code == ISIS_AUTH_FAILURE)
+                       isis_notif_authentication_failure(circuit, raw_pdu);
+               else /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
+                       isis_notif_authentication_type_failure(circuit,
+                                                              raw_pdu);
+#endif /* ifndef FABRICD */
                goto out;
        }
 
@@ -654,6 +722,10 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
                zlog_warn(
                        "ISIS-Adj (%s): Received IIH with own sysid - discard",
                        circuit->area->area_tag);
+#ifndef FABRICD
+               isis_notif_reject_adjacency(
+                       circuit, "Received IIH with our own sysid", raw_pdu);
+#endif /* ifndef FABRICD */
                goto out;
        }
 
@@ -668,10 +740,14 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
                                circuit->area->area_tag, level,
                                circuit->interface->name);
                }
+#ifndef FABRICD
+               /* send northbound notification */
+               isis_notif_area_mismatch(circuit, raw_pdu);
+#endif /* ifndef FABRICD */
                goto out;
        }
 
-       iih.v4_usable = (circuit->ip_addrs && listcount(circuit->ip_addrs)
+       iih.v4_usable = (fabricd_ip_addrs(circuit)
                         && iih.tlvs->ipv4_address.count);
 
        iih.v6_usable = (circuit->ipv6_link && listcount(circuit->ipv6_link)
@@ -683,7 +759,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
                                "ISIS-Adj (%s): Neither IPv4 nor IPv6 considered usable. Ignoring IIH",
                                circuit->area->area_tag);
                }
-
+#ifndef FABRICD
+               isis_notif_reject_adjacency(
+                       circuit, "Neither IPv4 not IPv6 considered usable",
+                       raw_pdu);
+#endif /* ifndef FABRICD */
                goto out;
        }
 
@@ -694,20 +774,59 @@ out:
        return retval;
 }
 
+static void lsp_flood_or_update(struct isis_lsp *lsp,
+                               struct isis_circuit *circuit,
+                               bool circuit_scoped)
+{
+       if (!circuit_scoped)
+               lsp_flood(lsp, circuit);
+       else
+               fabricd_update_lsp_no_flood(lsp, circuit);
+}
+
 /*
  * Process Level 1/2 Link State
  * ISO - 10589
  * Section 7.3.15.1 - Action on receipt of a link state PDU
  */
 static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
-                      const uint8_t *ssnpa)
+                      const uint8_t *ssnpa, uint8_t max_area_addrs)
 {
-       int level = (pdu_type == L1_LINK_STATE) ? ISIS_LEVEL1 : ISIS_LEVEL2;
+       int level;
+       bool circuit_scoped;
+       size_t pdu_start = stream_get_getp(circuit->rcv_stream);
+       size_t pdu_end = stream_get_endp(circuit->rcv_stream);
+       char raw_pdu[pdu_end - pdu_start];
+
+       stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
+                       pdu_end - pdu_start);
+
+       if (pdu_type == FS_LINK_STATE) {
+               if (!fabricd)
+                       return ISIS_ERROR;
+               if (max_area_addrs != L2_CIRCUIT_FLOODING_SCOPE)
+                       return ISIS_ERROR;
+               level = ISIS_LEVEL2;
+               circuit_scoped = true;
+
+               /* The stream is used verbatim for sending out new LSPDUs.
+                * So make sure we store it as an L2 LSPDU internally.
+                * (compare for the reverse in `send_lsp`) */
+               stream_putc_at(circuit->rcv_stream, 4, L2_LINK_STATE);
+               stream_putc_at(circuit->rcv_stream, 7, 0);
+       } else {
+               if (pdu_type == L1_LINK_STATE)
+                       level = ISIS_LEVEL1;
+               else
+                       level = ISIS_LEVEL2;
+               circuit_scoped = false;
+       }
 
        if (isis->debugs & DEBUG_UPDATE_PACKETS) {
                zlog_debug(
-                       "ISIS-Upd (%s): Rcvd L%d LSP on %s, cirType %s, cirID %u",
-                       circuit->area->area_tag, level,
+                       "ISIS-Upd (%s): Rcvd %sL%d LSP on %s, cirType %s, cirID %u",
+                       circuit->area->area_tag,
+                       circuit_scoped ? "Circuit scoped " : "", level,
                        circuit->interface->name,
                        circuit_t2string(circuit->is_type),
                        circuit->circuit_id);
@@ -725,6 +844,12 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
        hdr.checksum = stream_getw(circuit->rcv_stream);
        hdr.lsp_bits = stream_getc(circuit->rcv_stream);
 
+#ifndef FABRICD
+       /* send northbound notification */
+       isis_notif_lsp_received(circuit, rawlspid_print(hdr.lsp_id), hdr.seqno,
+                               time(NULL), sysid_print(hdr.lsp_id));
+#endif /* ifndef FABRICD */
+
        if (pdu_len_validate(hdr.pdu_len, circuit)) {
                zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %" PRIu16,
                           circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
@@ -803,10 +928,20 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
        struct isis_passwd *passwd = (level == ISIS_LEVEL1)
                                             ? &circuit->area->area_passwd
                                             : &circuit->area->domain_passwd;
-       if (!isis_tlvs_auth_is_valid(tlvs, passwd, circuit->rcv_stream, true)) {
+       int auth_code = isis_tlvs_auth_is_valid(tlvs, passwd,
+                                               circuit->rcv_stream, true);
+       if (auth_code != ISIS_AUTH_OK) {
                isis_event_auth_failure(circuit->area->area_tag,
                                        "LSP authentication failure",
                                        hdr.lsp_id);
+#ifndef FABRICD
+               /* send northbound notification */
+               if (auth_code == ISIS_AUTH_FAILURE)
+                       isis_notif_authentication_failure(circuit, raw_pdu);
+               else /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
+                       isis_notif_authentication_type_failure(circuit,
+                                                              raw_pdu);
+#endif /* ifndef FABRICD */
                goto out;
        }
 
@@ -869,7 +1004,8 @@ dontcheckadj:
         * but
         *            wrong checksum, initiate a purge. */
        if (lsp && (lsp->hdr.seqno == hdr.seqno)
-           && (lsp->hdr.checksum != hdr.checksum)) {
+           && (lsp->hdr.checksum != hdr.checksum)
+           && hdr.rem_lifetime) {
                zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08" PRIx32
                          " with confused checksum received.",
                          circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
@@ -899,7 +1035,8 @@ dontcheckadj:
                                                   lsp_confusion);
                                        tlvs = NULL;
                                        /* ii */
-                                       lsp_set_all_srmflags(lsp);
+                                       lsp_flood_or_update(lsp, NULL,
+                                                           circuit_scoped);
                                        /* v */
                                        ISIS_FLAGS_CLEAR_ALL(
                                                lsp->SSNflags); /* FIXME:
@@ -913,9 +1050,10 @@ dontcheckadj:
                                         * Otherwise, don't reflood
                                         * through incoming circuit as usual */
                                        if (!lsp_confusion) {
-                                               /* iii */
-                                               ISIS_CLEAR_FLAG(lsp->SRMflags,
-                                                               circuit);
+                                               isis_tx_queue_del(
+                                                       circuit->tx_queue,
+                                                       lsp);
+
                                                /* iv */
                                                if (circuit->circ_type
                                                    != CIRCUIT_T_BROADCAST)
@@ -926,7 +1064,8 @@ dontcheckadj:
                                } /* 7.3.16.4 b) 2) */
                                else if (comp == LSP_EQUAL) {
                                        /* i */
-                                       ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
+                                       isis_tx_queue_del(circuit->tx_queue,
+                                                         lsp);
                                        /* ii */
                                        if (circuit->circ_type
                                            != CIRCUIT_T_BROADCAST)
@@ -934,21 +1073,26 @@ dontcheckadj:
                                                              circuit);
                                } /* 7.3.16.4 b) 3) */
                                else {
-                                       ISIS_SET_FLAG(lsp->SRMflags, circuit);
+                                       isis_tx_queue_add(circuit->tx_queue,
+                                                         lsp, TX_LSP_NORMAL);
                                        ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
                                }
                        } else if (lsp->hdr.rem_lifetime != 0) {
                                /* our own LSP -> 7.3.16.4 c) */
                                if (comp == LSP_NEWER) {
                                        lsp_inc_seqno(lsp, hdr.seqno);
-                                       lsp_set_all_srmflags(lsp);
+                                       lsp_flood_or_update(lsp, NULL,
+                                                           circuit_scoped);
                                } else {
-                                       ISIS_SET_FLAG(lsp->SRMflags, circuit);
+                                       isis_tx_queue_add(circuit->tx_queue,
+                                                         lsp, TX_LSP_NORMAL);
                                        ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
                                }
                                if (isis->debugs & DEBUG_UPDATE_PACKETS)
                                        zlog_debug(
-                                               "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08" PRIx32,
+                                               "ISIS-Upd (%s): (1) "
+                                               "re-originating LSP %s new seq "
+                                               "0x%08" PRIx32,
                                                circuit->area->area_tag,
                                                rawlspid_print(hdr.lsp_id),
                                                lsp->hdr.seqno);
@@ -973,19 +1117,26 @@ dontcheckadj:
                 * is
                 * "greater" than that held by S, ... */
 
-               if (hdr.seqno > lsp->hdr.seqno) {
+               if (comp == LSP_NEWER) {
                        /* 7.3.16.1  */
                        lsp_inc_seqno(lsp, hdr.seqno);
-                       if (isis->debugs & DEBUG_UPDATE_PACKETS)
+                       if (isis->debugs & DEBUG_UPDATE_PACKETS) {
                                zlog_debug(
                                        "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08" PRIx32,
                                        circuit->area->area_tag,
                                        rawlspid_print(hdr.lsp_id),
                                        lsp->hdr.seqno);
+                       }
+                       lsp_flood(lsp, NULL);
+               } else if (comp == LSP_EQUAL) {
+                       isis_tx_queue_del(circuit->tx_queue, lsp);
+                       if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+                               ISIS_SET_FLAG(lsp->SSNflags, circuit);
+               } else {
+                       isis_tx_queue_add(circuit->tx_queue, lsp,
+                                         TX_LSP_NORMAL);
+                       ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
                }
-               /* If the received LSP is older or equal,
-                * resend the LSP which will act as ACK */
-               lsp_set_all_srmflags(lsp);
        } else {
                /* 7.3.15.1 e) - This lsp originated on another system */
 
@@ -1006,7 +1157,7 @@ dontcheckadj:
                                if (!lsp0) {
                                        zlog_debug(
                                                "Got lsp frag, while zero lsp not in database");
-                                       return ISIS_OK;
+                                       goto out;
                                }
                        }
                        /* i */
@@ -1023,10 +1174,7 @@ dontcheckadj:
                                           circuit->area, level, false);
                                tlvs = NULL;
                        }
-                       /* ii */
-                       lsp_set_all_srmflags(lsp);
-                       /* iii */
-                       ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
+                       lsp_flood_or_update(lsp, circuit, circuit_scoped);
 
                        /* iv */
                        if (circuit->circ_type != CIRCUIT_T_BROADCAST)
@@ -1035,7 +1183,7 @@ dontcheckadj:
                }
                /* 7.3.15.1 e) 2) LSP equal to the one in db */
                else if (comp == LSP_EQUAL) {
-                       ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
+                       isis_tx_queue_del(circuit->tx_queue, lsp);
                        lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream,
                                   circuit->area, level, false);
                        tlvs = NULL;
@@ -1044,7 +1192,8 @@ dontcheckadj:
                }
                /* 7.3.15.1 e) 3) LSP older than the one in db */
                else {
-                       ISIS_SET_FLAG(lsp->SRMflags, circuit);
+                       isis_tx_queue_add(circuit->tx_queue, lsp,
+                                         TX_LSP_NORMAL);
                        ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
                }
        }
@@ -1052,6 +1201,8 @@ dontcheckadj:
        retval = ISIS_OK;
 
 out:
+       fabricd_trigger_csnp(circuit->area, circuit_scoped);
+
        isis_free_tlvs(tlvs);
        return retval;
 }
@@ -1065,6 +1216,12 @@ out:
 static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
                       const uint8_t *ssnpa)
 {
+#ifndef FABRICD
+       size_t pdu_start = stream_get_getp(circuit->rcv_stream);
+       size_t pdu_end = stream_get_endp(circuit->rcv_stream);
+       char raw_pdu[pdu_end - pdu_start];
+#endif /* ifndef FABRICD */
+
        bool is_csnp = (pdu_type == L1_COMPLETE_SEQ_NUM
                        || pdu_type == L2_COMPLETE_SEQ_NUM);
        char typechar = is_csnp ? 'C' : 'P';
@@ -1075,6 +1232,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
 
        uint16_t pdu_len = stream_getw(circuit->rcv_stream);
        uint8_t rem_sys_id[ISIS_SYS_ID_LEN];
+
        stream_get(rem_sys_id, circuit->rcv_stream, ISIS_SYS_ID_LEN);
        stream_forward_getp(circuit->rcv_stream, 1); /* Circuit ID - unused */
 
@@ -1157,7 +1315,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
                                     circuit->u.bc.adjdb[level - 1]))
                        return ISIS_OK; /* Silently discard */
        } else {
-               if (!circuit->u.p2p.neighbor) {
+               if (!fabricd && !circuit->u.p2p.neighbor) {
                        zlog_warn("no p2p neighbor on circuit %s",
                                  circuit->interface->name);
                        return ISIS_OK; /* Silently discard */
@@ -1178,13 +1336,26 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
        struct isis_passwd *passwd = (level == IS_LEVEL_1)
                                             ? &circuit->area->area_passwd
                                             : &circuit->area->domain_passwd;
-       if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)
-           && !isis_tlvs_auth_is_valid(tlvs, passwd, circuit->rcv_stream,
-                                       false)) {
-               isis_event_auth_failure(circuit->area->area_tag,
-                                       "SNP authentication failure",
-                                       rem_sys_id);
-               goto out;
+       if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) {
+               int auth_code = isis_tlvs_auth_is_valid(
+                       tlvs, passwd, circuit->rcv_stream, false);
+               if (auth_code != ISIS_AUTH_OK) {
+                       isis_event_auth_failure(circuit->area->area_tag,
+                                               "SNP authentication failure",
+                                               rem_sys_id);
+#ifndef FABRICD
+                       /* send northbound notification */
+                       stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
+                                       pdu_end - pdu_start);
+                       if (auth_code == ISIS_AUTH_FAILURE)
+                               isis_notif_authentication_failure(circuit,
+                                                                 raw_pdu);
+                       else /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
+                               isis_notif_authentication_type_failure(circuit,
+                                                                      raw_pdu);
+#endif /* ifndef FABRICD */
+                       goto out;
+               }
        }
 
        struct isis_lsp_entry *entry_head =
@@ -1206,6 +1377,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
                }
        }
 
+       bool resync_needed = false;
+
        /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */
        for (struct isis_lsp_entry *entry = entry_head; entry;
             entry = entry->next) {
@@ -1221,25 +1394,28 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
                        if (cmp == LSP_EQUAL) {
                                /* if (circuit->circ_type !=
                                 * CIRCUIT_T_BROADCAST) */
-                               ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
+                               isis_tx_queue_del(circuit->tx_queue, lsp);
                        }
                        /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM
                           */
                        else if (cmp == LSP_OLDER) {
                                ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
-                               ISIS_SET_FLAG(lsp->SRMflags, circuit);
+                               isis_tx_queue_add(circuit->tx_queue, lsp,
+                                                 TX_LSP_NORMAL);
                        }
                        /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM
                           on p2p */
                        else {
                                if (own_lsp) {
                                        lsp_inc_seqno(lsp, entry->seqno);
-                                       ISIS_SET_FLAG(lsp->SRMflags, circuit);
+                                       isis_tx_queue_add(circuit->tx_queue, lsp,
+                                                       TX_LSP_NORMAL);
                                } else {
                                        ISIS_SET_FLAG(lsp->SSNflags, circuit);
                                        /* if (circuit->circ_type !=
                                         * CIRCUIT_T_BROADCAST) */
-                                       ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
+                                       isis_tx_queue_del(circuit->tx_queue, lsp);
+                                       resync_needed = true;
                                }
                        }
                } else {
@@ -1265,14 +1441,15 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
                                                continue;
                                        }
                                }
-                               struct isis_lsp *lsp =
-                                       lsp_new(circuit->area, entry->id,
+                               lsp = lsp_new(circuit->area, entry->id,
                                                entry->rem_lifetime, 0, 0,
                                                entry->checksum, lsp0, level);
                                lsp_insert(lsp,
                                           circuit->area->lspdb[level - 1]);
-                               ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags);
+
+                               lsp_set_all_srmflags(lsp, false);
                                ISIS_SET_FLAG(lsp->SSNflags, circuit);
+                               resync_needed = true;
                        }
                }
        }
@@ -1303,12 +1480,18 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
                }
 
                /* on remaining LSPs we set SRM (neighbor knew not of) */
-               for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp))
-                       ISIS_SET_FLAG(lsp->SRMflags, circuit);
+               for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) {
+                       isis_tx_queue_add(circuit->tx_queue, lsp, TX_LSP_NORMAL);
+                       resync_needed = true;
+               }
+
                /* lets free it */
-               list_delete_and_null(&lsp_list);
+               list_delete(&lsp_list);
        }
 
+       if (fabricd_initial_sync_is_complete(circuit->area) && resync_needed)
+               zlog_warn("OpenFabric: Needed to resync LSPDB using CSNP!\n");
+
        retval = ISIS_OK;
 out:
        isis_free_tlvs(tlvs);
@@ -1327,6 +1510,7 @@ static int pdu_size(uint8_t pdu_type, uint8_t *size)
                break;
        case L1_LINK_STATE:
        case L2_LINK_STATE:
+       case FS_LINK_STATE:
                *size = ISIS_LSP_HDR_LEN;
                break;
        case L1_COMPLETE_SEQ_NUM:
@@ -1351,10 +1535,16 @@ static int pdu_size(uint8_t pdu_type, uint8_t *size)
 int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
 {
        int retval = ISIS_OK;
+       size_t pdu_start = stream_get_getp(circuit->rcv_stream);
+       size_t pdu_end = stream_get_endp(circuit->rcv_stream);
+       char raw_pdu[pdu_end - pdu_start];
+
+       stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
+                       pdu_end - pdu_start);
 
        /* Verify that at least the 8 bytes fixed header have been received */
        if (stream_get_endp(circuit->rcv_stream) < ISIS_FIXED_HDR_LEN) {
-               flog_err(ISIS_ERR_PACKET, "PDU is too short to be IS-IS.");
+               flog_err(EC_ISIS_PACKET, "PDU is too short to be IS-IS.");
                return ISIS_ERROR;
        }
 
@@ -1365,18 +1555,21 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
        uint8_t pdu_type = stream_getc(circuit->rcv_stream)
                           & 0x1f; /* bits 6-8 are reserved */
        uint8_t version2 = stream_getc(circuit->rcv_stream);
+
        stream_forward_getp(circuit->rcv_stream, 1); /* reserved */
        uint8_t max_area_addrs = stream_getc(circuit->rcv_stream);
 
+       pdu_counter_count(circuit->area->pdu_rx_counters, pdu_type);
+
        if (idrp == ISO9542_ESIS) {
-               flog_err(LIB_ERR_DEVELOPMENT,
-                         "No support for ES-IS packet IDRP=%" PRIx8, idrp);
+               flog_err(EC_LIB_DEVELOPMENT,
+                        "No support for ES-IS packet IDRP=%" PRIx8, idrp);
                return ISIS_ERROR;
        }
 
        if (idrp != ISO10589_ISIS) {
-               flog_err(ISIS_ERR_PACKET, "Not an IS-IS packet IDRP=%" PRIx8,
-                         idrp);
+               flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%" PRIx8,
+                        idrp);
                return ISIS_ERROR;
        }
 
@@ -1387,7 +1580,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
 
        if (id_len != 0 && id_len != ISIS_SYS_ID_LEN) {
                flog_err(
-                       ISIS_ERR_PACKET,
+                       EC_ISIS_PACKET,
                        "IDFieldLengthMismatch: ID Length field in a received PDU  %" PRIu8
                        ", while the parameter for this IS is %u",
                        id_len, ISIS_SYS_ID_LEN);
@@ -1401,16 +1594,16 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
        }
 
        if (length != expected_length) {
-               flog_err(ISIS_ERR_PACKET,
-                         "Exepected fixed header length = %" PRIu8
-                         " but got %" PRIu8,
-                         expected_length, length);
+               flog_err(EC_ISIS_PACKET,
+                        "Exepected fixed header length = %" PRIu8
+                        " but got %" PRIu8,
+                        expected_length, length);
                return ISIS_ERROR;
        }
 
        if (stream_get_endp(circuit->rcv_stream) < length) {
                flog_err(
-                       ISIS_ERR_PACKET,
+                       EC_ISIS_PACKET,
                        "PDU is too short to contain fixed header of given PDU type.");
                return ISIS_ERROR;
        }
@@ -1427,12 +1620,19 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
        }
 
        /* either 3 or 0 */
-       if (max_area_addrs != 0 && max_area_addrs != isis->max_area_addrs) {
+       if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr field */
+           && max_area_addrs != 0
+           && max_area_addrs != isis->max_area_addrs) {
                flog_err(
-                       ISIS_ERR_PACKET,
+                       EC_ISIS_PACKET,
                        "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %" PRIu8
                        " while the parameter for this IS is %u",
                        max_area_addrs, isis->max_area_addrs);
+#ifndef FABRICD
+               /* send northbound notification */
+               isis_notif_max_area_addr_mismatch(circuit, max_area_addrs,
+                                                 raw_pdu);
+#endif /* ifndef FABRICD */
                return ISIS_ERROR;
        }
 
@@ -1440,11 +1640,18 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
        case L1_LAN_HELLO:
        case L2_LAN_HELLO:
        case P2P_HELLO:
+               if (fabricd && pdu_type != P2P_HELLO)
+                       return ISIS_ERROR;
                retval = process_hello(pdu_type, circuit, ssnpa);
                break;
        case L1_LINK_STATE:
        case L2_LINK_STATE:
-               retval = process_lsp(pdu_type, circuit, ssnpa);
+       case FS_LINK_STATE:
+               if (fabricd
+                   && pdu_type != L2_LINK_STATE
+                   && pdu_type != FS_LINK_STATE)
+                       return ISIS_ERROR;
+               retval = process_lsp(pdu_type, circuit, ssnpa, max_area_addrs);
                break;
        case L1_COMPLETE_SEQ_NUM:
        case L2_COMPLETE_SEQ_NUM:
@@ -1511,15 +1718,18 @@ void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream)
        stream_putc(stream, 0); /* Max Area Addresses 0 => 3 */
 }
 
-static void put_hello_hdr(struct isis_circuit *circuit, int level,
-                         size_t *len_pointer)
+static uint8_t hello_pdu_type(struct isis_circuit *circuit, int level)
 {
-       uint8_t pdu_type;
-
        if (circuit->circ_type == CIRCUIT_T_BROADCAST)
-               pdu_type = (level == IS_LEVEL_1) ? L1_LAN_HELLO : L2_LAN_HELLO;
+               return (level == IS_LEVEL_1) ? L1_LAN_HELLO : L2_LAN_HELLO;
        else
-               pdu_type = P2P_HELLO;
+               return P2P_HELLO;
+}
+
+static void put_hello_hdr(struct isis_circuit *circuit, int level,
+                         size_t *len_pointer)
+{
+       uint8_t pdu_type = hello_pdu_type(circuit, level);
 
        isis_circuit_stream(circuit, &circuit->snd_stream);
        fill_fixed_hdr(pdu_type, circuit->snd_stream);
@@ -1582,8 +1792,15 @@ int send_hello(struct isis_circuit *circuit, int level)
                   && !circuit->disable_threeway_adj) {
                uint32_t ext_circuit_id = circuit->idx;
                if (circuit->u.p2p.neighbor) {
+                       uint8_t threeway_state;
+
+                       if (fabricd_initial_sync_is_in_progress(circuit->area)
+                           && fabricd_initial_sync_circuit(circuit->area) != circuit)
+                               threeway_state = ISIS_THREEWAY_DOWN;
+                       else
+                               threeway_state = circuit->u.p2p.neighbor->threeway_state;
                        isis_tlvs_add_threeway_adj(tlvs,
-                                       circuit->u.p2p.neighbor->threeway_state,
+                                       threeway_state,
                                        ext_circuit_id,
                                        circuit->u.p2p.neighbor->sysid,
                                        circuit->u.p2p.neighbor->ext_circuit_id);
@@ -1618,8 +1835,12 @@ int send_hello(struct isis_circuit *circuit, int level)
                                                     false, false);
        }
 
-       if (circuit->ip_router && circuit->ip_addrs)
-               isis_tlvs_add_ipv4_addresses(tlvs, circuit->ip_addrs);
+       if (circuit->ip_router) {
+               struct list *circuit_ip_addrs = fabricd_ip_addrs(circuit);
+
+               if (circuit_ip_addrs)
+                       isis_tlvs_add_ipv4_addresses(tlvs, circuit_ip_addrs);
+       }
 
        if (circuit->ipv6_router && circuit->ipv6_link)
                isis_tlvs_add_ipv6_addresses(tlvs, circuit->ipv6_link);
@@ -1651,91 +1872,105 @@ int send_hello(struct isis_circuit *circuit, int level)
 
        isis_free_tlvs(tlvs);
 
+       pdu_counter_count(circuit->area->pdu_tx_counters,
+                         hello_pdu_type(circuit, level));
        retval = circuit->tx(circuit, level);
        if (retval != ISIS_OK)
-               flog_err(ISIS_ERR_PACKET,
-                         "ISIS-Adj (%s): Send L%d IIH on %s failed",
-                         circuit->area->area_tag, level,
-                         circuit->interface->name);
+               flog_err(EC_ISIS_PACKET,
+                        "ISIS-Adj (%s): Send L%d IIH on %s failed",
+                        circuit->area->area_tag, level,
+                        circuit->interface->name);
 
        return retval;
 }
 
-int send_lan_l1_hello(struct thread *thread)
+static int send_hello_cb(struct thread *thread)
 {
-       struct isis_circuit *circuit;
-       int retval;
+       struct isis_circuit_arg *arg = THREAD_ARG(thread);
+       assert(arg);
+
+       struct isis_circuit *circuit = arg->circuit;
+       int level = arg->level;
 
-       circuit = THREAD_ARG(thread);
        assert(circuit);
-       circuit->u.bc.t_send_lan_hello[0] = NULL;
 
-       if (!(circuit->area->is_type & IS_LEVEL_1)) {
-               zlog_warn(
-                       "ISIS-Hello (%s): Trying to send L1 IIH in L2-only area",
-                       circuit->area->area_tag);
-               return 1;
+       if (circuit->circ_type == CIRCUIT_T_P2P) {
+               circuit->u.p2p.t_send_p2p_hello = NULL;
+               send_hello(circuit, 1);
+               send_hello_sched(circuit, ISIS_LEVEL1,
+                                1000 * circuit->hello_interval[1]);
+               return ISIS_OK;
+       }
+
+       if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
+               zlog_warn("ISIS-Hello (%s): Trying to send hello on unknown circuit type %d",
+                         circuit->area->area_tag, circuit->circ_type);
+               return ISIS_WARNING;
+       }
+
+       circuit->u.bc.t_send_lan_hello[level - 1] = NULL;
+       if (!(circuit->is_type & level)) {
+               zlog_warn("ISIS-Hello (%s): Trying to send L%d IIH in L%d-only circuit",
+                         circuit->area->area_tag, level, 3 - level);
+               return ISIS_WARNING;
        }
 
-       if (circuit->u.bc.run_dr_elect[0])
-               isis_dr_elect(circuit, 1);
+       if (circuit->u.bc.run_dr_elect[level - 1])
+               isis_dr_elect(circuit, level);
 
-       retval = send_hello(circuit, 1);
+       int rv = send_hello(circuit, level);
 
        /* set next timer thread */
-       thread_add_timer(master, send_lan_l1_hello, circuit,
-                        isis_jitter(circuit->hello_interval[0], IIH_JITTER),
-                        &circuit->u.bc.t_send_lan_hello[0]);
-
-       return retval;
+       send_hello_sched(circuit, level, 1000 * circuit->hello_interval[level - 1]);
+       return rv;
 }
 
-int send_lan_l2_hello(struct thread *thread)
+static void _send_hello_sched(struct isis_circuit *circuit,
+                             struct thread **threadp,
+                             int level, long delay)
 {
-       struct isis_circuit *circuit;
-       int retval;
+       if (*threadp) {
+               if (thread_timer_remain_msec(*threadp) < (unsigned long)delay)
+                       return;
 
-       circuit = THREAD_ARG(thread);
-       assert(circuit);
-       circuit->u.bc.t_send_lan_hello[1] = NULL;
-
-       if (!(circuit->area->is_type & IS_LEVEL_2)) {
-               zlog_warn("ISIS-Hello (%s): Trying to send L2 IIH in L1 area",
-                         circuit->area->area_tag);
-               return 1;
+               thread_cancel(*threadp);
        }
 
-       if (circuit->u.bc.run_dr_elect[1])
-               isis_dr_elect(circuit, 2);
-
-       retval = send_hello(circuit, 2);
-
-       /* set next timer thread */
-       thread_add_timer(master, send_lan_l2_hello, circuit,
-                        isis_jitter(circuit->hello_interval[1], IIH_JITTER),
-                        &circuit->u.bc.t_send_lan_hello[1]);
-
-       return retval;
+       thread_add_timer_msec(master, send_hello_cb,
+                             &circuit->level_arg[level - 1],
+                             isis_jitter(delay, IIH_JITTER),
+                             threadp);
 }
 
-int send_p2p_hello(struct thread *thread)
+void send_hello_sched(struct isis_circuit *circuit, int level, long delay)
 {
-       struct isis_circuit *circuit;
-
-       circuit = THREAD_ARG(thread);
-       assert(circuit);
-       circuit->u.p2p.t_send_p2p_hello = NULL;
+       if (circuit->circ_type == CIRCUIT_T_P2P) {
+               _send_hello_sched(circuit, &circuit->u.p2p.t_send_p2p_hello,
+                                 ISIS_LEVEL1, delay);
+               return;
+       }
 
-       send_hello(circuit, 1);
+       if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
+               zlog_warn("%s: encountered unknown circuit type %d on %s",
+                         __func__, circuit->circ_type,
+                         circuit->interface->name);
+               return;
+       }
 
-       /* set next timer thread */
-       thread_add_timer(master, send_p2p_hello, circuit,
-                        isis_jitter(circuit->hello_interval[1], IIH_JITTER),
-                        &circuit->u.p2p.t_send_p2p_hello);
+       for (int loop_level = ISIS_LEVEL1; loop_level <= ISIS_LEVEL2; loop_level++) {
+               if (!(loop_level & level))
+                       continue;
 
-       return ISIS_OK;
+               _send_hello_sched(
+                       circuit,
+                       &circuit->u.bc.t_send_lan_hello[loop_level - 1],
+                       loop_level,
+                       delay
+               );
+       }
 }
 
+
 /*
  * Count the maximum number of lsps that can be accomodated by a given size.
  */
@@ -1764,10 +1999,11 @@ int send_csnp(struct isis_circuit *circuit, int level)
            || dict_count(circuit->area->lspdb[level - 1]) == 0)
                return ISIS_OK;
 
+       uint8_t pdu_type = (level == ISIS_LEVEL1) ? L1_COMPLETE_SEQ_NUM
+                                                 : L2_COMPLETE_SEQ_NUM;
+
        isis_circuit_stream(circuit, &circuit->snd_stream);
-       fill_fixed_hdr((level == ISIS_LEVEL1) ? L1_COMPLETE_SEQ_NUM
-                                             : L2_COMPLETE_SEQ_NUM,
-                      circuit->snd_stream);
+       fill_fixed_hdr(pdu_type, circuit->snd_stream);
 
        size_t len_pointer = stream_get_endp(circuit->snd_stream);
        stream_putw(circuit->snd_stream, 0);
@@ -1849,12 +2085,13 @@ int send_csnp(struct isis_circuit *circuit, int level)
                                        stream_get_endp(circuit->snd_stream));
                }
 
+               pdu_counter_count(circuit->area->pdu_tx_counters, pdu_type);
                int retval = circuit->tx(circuit, level);
                if (retval != ISIS_OK) {
-                       flog_err(ISIS_ERR_PACKET,
-                                 "ISIS-Snp (%s): Send L%d CSNP on %s failed",
-                                 circuit->area->area_tag, level,
-                                 circuit->interface->name);
+                       flog_err(EC_ISIS_PACKET,
+                                "ISIS-Snp (%s): Send L%d CSNP on %s failed",
+                                circuit->area->area_tag, level,
+                                circuit->interface->name);
                        isis_free_tlvs(tlvs);
                        return retval;
                }
@@ -1889,8 +2126,9 @@ int send_l1_csnp(struct thread *thread)
 
        circuit->t_send_csnp[0] = NULL;
 
-       if (circuit->circ_type == CIRCUIT_T_BROADCAST
-           && circuit->u.bc.is_dr[0]) {
+       if ((circuit->circ_type == CIRCUIT_T_BROADCAST
+            && circuit->u.bc.is_dr[0])
+            || circuit->circ_type == CIRCUIT_T_P2P) {
                send_csnp(circuit, 1);
        }
        /* set next timer thread */
@@ -1911,8 +2149,9 @@ int send_l2_csnp(struct thread *thread)
 
        circuit->t_send_csnp[1] = NULL;
 
-       if (circuit->circ_type == CIRCUIT_T_BROADCAST
-           && circuit->u.bc.is_dr[1]) {
+       if ((circuit->circ_type == CIRCUIT_T_BROADCAST
+            && circuit->u.bc.is_dr[1])
+             || circuit->circ_type == CIRCUIT_T_P2P) {
                send_csnp(circuit, 2);
        }
        /* set next timer thread */
@@ -1940,10 +2179,11 @@ static int send_psnp(int level, struct isis_circuit *circuit)
        if (!circuit->snd_stream)
                return ISIS_ERROR;
 
+       uint8_t pdu_type = (level == ISIS_LEVEL1) ? L1_PARTIAL_SEQ_NUM
+                                                 : L2_PARTIAL_SEQ_NUM;
+
        isis_circuit_stream(circuit, &circuit->snd_stream);
-       fill_fixed_hdr((level == ISIS_LEVEL1) ? L1_PARTIAL_SEQ_NUM
-                                             : L2_PARTIAL_SEQ_NUM,
-                      circuit->snd_stream);
+       fill_fixed_hdr(pdu_type, circuit->snd_stream);
 
        size_t len_pointer = stream_get_endp(circuit->snd_stream);
        stream_putw(circuit->snd_stream, 0); /* length is filled in later */
@@ -2014,12 +2254,13 @@ static int send_psnp(int level, struct isis_circuit *circuit)
                                        stream_get_endp(circuit->snd_stream));
                }
 
+               pdu_counter_count(circuit->area->pdu_tx_counters, pdu_type);
                int retval = circuit->tx(circuit, level);
                if (retval != ISIS_OK) {
-                       flog_err(ISIS_ERR_PACKET,
-                                 "ISIS-Snp (%s): Send L%d PSNP on %s failed",
-                                 circuit->area->area_tag, level,
-                                 circuit->interface->name);
+                       flog_err(EC_ISIS_PACKET,
+                                "ISIS-Snp (%s): Send L%d PSNP on %s failed",
+                                circuit->area->area_tag, level,
+                                circuit->interface->name);
                        isis_free_tlvs(tlvs);
                        return retval;
                }
@@ -2086,25 +2327,12 @@ int send_l2_psnp(struct thread *thread)
 /*
  * ISO 10589 - 7.3.14.3
  */
-int send_lsp(struct thread *thread)
+void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp,
+             enum isis_tx_type tx_type)
 {
-       struct isis_circuit *circuit;
-       struct isis_lsp *lsp;
        int clear_srm = 1;
        int retval = ISIS_OK;
 
-       circuit = THREAD_ARG(thread);
-       assert(circuit);
-       circuit->t_send_lsp = NULL;
-
-       lsp = isis_circuit_lsp_queue_pop(circuit);
-       if (!lsp)
-               return ISIS_OK;
-
-       if (!list_isempty(circuit->lsp_queue)) {
-               isis_circuit_schedule_lsp_send(circuit);
-       }
-
        if (circuit->state != C_STATE_UP || circuit->is_passive == 1)
                goto out;
 
@@ -2125,7 +2353,7 @@ int send_lsp(struct thread *thread)
         * the circuit's MTU. So handle and log this case here. */
        if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) {
                flog_err(
-                       ISIS_ERR_PACKET,
+                       EC_ISIS_PACKET,
                        "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08" PRIx32
                        ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
                        "s on %s. LSP Size is %zu while interface stream size is %zu.",
@@ -2134,6 +2362,11 @@ int send_lsp(struct thread *thread)
                        lsp->hdr.checksum, lsp->hdr.rem_lifetime,
                        circuit->interface->name, stream_get_endp(lsp->pdu),
                        stream_get_size(circuit->snd_stream));
+#ifndef FABRICD
+               /* send a northbound notification */
+               isis_notif_lsp_too_large(circuit, stream_get_endp(lsp->pdu),
+                                        rawlspid_print(lsp->hdr.lsp_id));
+#endif /* ifndef FABRICD */
                if (isis->debugs & DEBUG_PACKET_DUMP)
                        zlog_dump_data(STREAM_DATA(lsp->pdu),
                                       stream_get_endp(lsp->pdu));
@@ -2144,11 +2377,20 @@ int send_lsp(struct thread *thread)
        /* copy our lsp to the send buffer */
        stream_copy(circuit->snd_stream, lsp->pdu);
 
+       if (tx_type == TX_LSP_CIRCUIT_SCOPED) {
+               stream_putc_at(circuit->snd_stream, 4, FS_LINK_STATE);
+               stream_putc_at(circuit->snd_stream, 7,
+                              L2_CIRCUIT_FLOODING_SCOPE);
+       }
+
        if (isis->debugs & DEBUG_UPDATE_PACKETS) {
-               zlog_debug("ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08" PRIx32
+               zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08" PRIx32
                           ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
                           "s on %s",
-                          circuit->area->area_tag, lsp->level,
+                          circuit->area->area_tag,
+                          (tx_type == TX_LSP_CIRCUIT_SCOPED)
+                               ? "Circuit scoped " : "",
+                          lsp->level,
                           rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno,
                           lsp->hdr.checksum, lsp->hdr.rem_lifetime,
                           circuit->interface->name);
@@ -2157,15 +2399,20 @@ int send_lsp(struct thread *thread)
                                       stream_get_endp(circuit->snd_stream));
        }
 
+       uint8_t pdu_type = (tx_type == TX_LSP_CIRCUIT_SCOPED) ? FS_LINK_STATE
+                        : (lsp->level == ISIS_LEVEL1) ? L1_LINK_STATE
+                                                      : L2_LINK_STATE;
+
        clear_srm = 0;
+       pdu_counter_count(circuit->area->pdu_tx_counters, pdu_type);
        retval = circuit->tx(circuit, lsp->level);
        if (retval != ISIS_OK) {
-               flog_err(ISIS_ERR_PACKET,
-                         "ISIS-Upd (%s): Send L%d LSP on %s failed %s",
-                         circuit->area->area_tag, lsp->level,
-                         circuit->interface->name,
-                         (retval == ISIS_WARNING) ? "temporarily"
-                                                  : "permanently");
+               flog_err(EC_ISIS_PACKET,
+                        "ISIS-Upd (%s): Send L%d LSP on %s failed %s",
+                        circuit->area->area_tag, lsp->level,
+                        circuit->interface->name,
+                        (retval == ISIS_WARNING) ? "temporarily"
+                                                 : "permanently");
        }
 
 out:
@@ -2181,8 +2428,6 @@ out:
                 * to clear
                 * the fag.
                 */
-               ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
+               isis_tx_queue_del(circuit->tx_queue, lsp);
        }
-
-       return retval;
 }