]> git.proxmox.com Git - mirror_frr.git/blobdiff - isisd/isis_lsp.c
zebra, lib: fix the ZEBRA_INTERFACE_VRF_UPDATE zapi message
[mirror_frr.git] / isisd / isis_lsp.c
index 61ba51ffb84f9ee74b002b8e672cf1dbe4d055ab..658624370b9ed0a3af7b5768779051ef03aa3866 100644 (file)
@@ -59,8 +59,7 @@
 #include "isisd/fabricd.h"
 #include "isisd/isis_tx_queue.h"
 
-static int lsp_l1_refresh(struct thread *thread);
-static int lsp_l2_refresh(struct thread *thread);
+static int lsp_refresh(struct thread *thread);
 static int lsp_l1_refresh_pseudo(struct thread *thread);
 static int lsp_l2_refresh_pseudo(struct thread *thread);
 
@@ -110,6 +109,8 @@ static void lsp_clear_data(struct isis_lsp *lsp)
        lsp->tlvs = NULL;
 }
 
+static void lsp_remove_frags(struct list *frags, dict_t *lspdb);
+
 static void lsp_destroy(struct isis_lsp *lsp)
 {
        struct listnode *cnode;
@@ -125,15 +126,25 @@ static void lsp_destroy(struct isis_lsp *lsp)
 
        lsp_clear_data(lsp);
 
-       if (LSP_FRAGMENT(lsp->hdr.lsp_id) == 0 && lsp->lspu.frags) {
-               list_delete(&lsp->lspu.frags);
-               lsp->lspu.frags = NULL;
+       if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) {
+               if (lsp->lspu.frags) {
+                       lsp_remove_frags(lsp->lspu.frags,
+                                        lsp->area->lspdb[lsp->level - 1]);
+                       list_delete(&lsp->lspu.frags);
+               }
+       } else {
+               if (lsp->lspu.zero_lsp
+                   && lsp->lspu.zero_lsp->lspu.frags) {
+                       listnode_delete(lsp->lspu.zero_lsp->lspu.frags, lsp);
+               }
        }
 
        isis_spf_schedule(lsp->area, lsp->level);
 
        if (lsp->pdu)
                stream_free(lsp->pdu);
+
+       fabricd_lsp_free(lsp);
        XFREE(MTYPE_ISIS_LSP, lsp);
 }
 
@@ -170,10 +181,6 @@ static void lsp_remove_frags(struct list *frags, dict_t *lspdb)
                lsp_destroy(lsp);
                dnode_destroy(dict_delete(lspdb, dnode));
        }
-
-       list_delete_all_node(frags);
-
-       return;
 }
 
 void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb)
@@ -248,7 +255,8 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno,
        if (seqno > lsp->hdr.seqno
            || (seqno == lsp->hdr.seqno
                && ((lsp->hdr.rem_lifetime != 0 && rem_lifetime == 0)
-                   || lsp->hdr.checksum != checksum))) {
+                   || (lsp->hdr.checksum != checksum
+                       && lsp->hdr.rem_lifetime)))) {
                if (isis->debugs & DEBUG_SNP_PACKETS) {
                        zlog_debug(
                                "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
@@ -347,6 +355,15 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno)
        else
                newseq = seqno + 1;
 
+#ifndef FABRICD
+       /* check for overflow */
+       if (newseq < lsp->hdr.seqno) {
+               /* send northbound notification */
+               isis_notif_lsp_exceed_max(lsp->area,
+                                         rawlspid_print(lsp->hdr.lsp_id));
+       }
+#endif /* ifndef FABRICD */
+
        lsp->hdr.seqno = newseq;
 
        lsp_pack_pdu(lsp);
@@ -378,6 +395,7 @@ static void lsp_purge(struct isis_lsp *lsp, int level,
        lsp->hdr.rem_lifetime = 0;
        lsp->level = level;
        lsp->age_out = lsp->area->max_lsp_lifetime[level - 1];
+       lsp->area->lsp_purge_count[level - 1]++;
 
        lsp_purge_add_poi(lsp, sender);
 
@@ -401,8 +419,12 @@ static void lsp_seqno_update(struct isis_lsp *lsp0)
        for (ALL_LIST_ELEMENTS_RO(lsp0->lspu.frags, node, lsp)) {
                if (lsp->tlvs)
                        lsp_inc_seqno(lsp, 0);
-               else
+               else if (lsp->hdr.rem_lifetime) {
+                       /* Purge should only be applied when the fragment has
+                        * non-zero remaining lifetime.
+                        */
                        lsp_purge(lsp, lsp0->level, NULL);
+               }
        }
 
        return;
@@ -516,6 +538,17 @@ struct isis_lsp *lsp_new_from_recv(struct isis_lsp_hdr *hdr,
        return lsp;
 }
 
+static void lsp_adjust_stream(struct isis_lsp *lsp)
+{
+       if (lsp->pdu) {
+               if (STREAM_SIZE(lsp->pdu) == LLC_LEN + lsp->area->lsp_mtu)
+                       return;
+               stream_free(lsp->pdu);
+       }
+
+       lsp->pdu = stream_new(LLC_LEN + lsp->area->lsp_mtu);
+}
+
 struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id,
                         uint16_t rem_lifetime, uint32_t seqno,
                         uint8_t lsp_bits, uint16_t checksum,
@@ -526,7 +559,7 @@ struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id,
        lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp));
        lsp->area = area;
 
-       lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu);
+       lsp_adjust_stream(lsp);
 
        /* Minimal LSP PDU size */
        lsp->hdr.pdu_len = ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN;
@@ -562,29 +595,17 @@ void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb)
 void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id,
                               struct list *list, dict_t *lspdb)
 {
-       dnode_t *first, *last, *curr;
-
-       first = dict_lower_bound(lspdb, start_id);
-       if (!first)
-               return;
+       for (dnode_t *curr = dict_lower_bound(lspdb, start_id);
+            curr; curr = dict_next(lspdb, curr)) {
+               struct isis_lsp *lsp = curr->dict_data;
 
-       last = dict_upper_bound(lspdb, stop_id);
-
-       curr = first;
-
-       if (((struct isis_lsp *)(curr->dict_data))->hdr.rem_lifetime)
-               listnode_add(list, first->dict_data);
-
-       while (curr) {
-               curr = dict_next(lspdb, curr);
-               if (curr
-                   && ((struct isis_lsp *)(curr->dict_data))->hdr.rem_lifetime)
-                       listnode_add(list, curr->dict_data);
-               if (curr == last)
+               if (memcmp(lsp->hdr.lsp_id, stop_id,
+                          ISIS_SYS_ID_LEN + 2) > 0)
                        break;
-       }
 
-       return;
+               if (lsp->hdr.rem_lifetime)
+                       listnode_add(list, lsp);
+       }
 }
 
 static void lsp_set_time(struct isis_lsp *lsp)
@@ -602,7 +623,7 @@ static void lsp_set_time(struct isis_lsp *lsp)
                stream_putw_at(lsp->pdu, 10, lsp->hdr.rem_lifetime);
 }
 
-static void lspid_print(uint8_t *lsp_id, uint8_t *trg, char dynhost, char frag)
+void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag)
 {
        struct isis_dynhn *dyn = NULL;
        uint8_t id[SYSID_STRLEN];
@@ -619,10 +640,10 @@ static void lspid_print(uint8_t *lsp_id, uint8_t *trg, char dynhost, char frag)
        else
                memcpy(id, sysid_print(lsp_id), 15);
        if (frag)
-               sprintf((char *)trg, "%s.%02x-%02x", id, LSP_PSEUDO_ID(lsp_id),
+               sprintf(dest, "%s.%02x-%02x", id, LSP_PSEUDO_ID(lsp_id),
                        LSP_FRAGMENT(lsp_id));
        else
-               sprintf((char *)trg, "%s.%02x", id, LSP_PSEUDO_ID(lsp_id));
+               sprintf(dest, "%s.%02x", id, LSP_PSEUDO_ID(lsp_id));
 }
 
 /* Convert the lsp attribute bits to attribute string */
@@ -651,7 +672,7 @@ static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size)
 /* this function prints the lsp on show isis database */
 void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost)
 {
-       uint8_t LSPid[255];
+       char LSPid[255];
        char age_out[8];
        char b[200];
 
@@ -1147,6 +1168,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
        struct isis_tlvs *tlvs = lsp->tlvs;
        lsp->tlvs = NULL;
 
+       lsp_adjust_stream(lsp);
        lsp_pack_pdu(lsp);
        size_t tlv_space = STREAM_WRITEABLE(lsp->pdu) - LLC_LEN;
        lsp_clear_data(lsp);
@@ -1178,6 +1200,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
 
                        frag = lsp_next_frag(LSP_FRAGMENT(frag->hdr.lsp_id) + 1,
                                             lsp, area, level);
+                       lsp_adjust_stream(frag);
                }
                frag->tlvs = tlvs;
        }
@@ -1227,17 +1250,15 @@ int lsp_generate(struct isis_area *area, int level)
        lsp_seqno_update(newlsp);
        newlsp->last_generated = time(NULL);
        lsp_flood(newlsp, NULL);
+       area->lsp_gen_count[level - 1]++;
 
        refresh_time = lsp_refresh_time(newlsp, rem_lifetime);
 
        THREAD_TIMER_OFF(area->t_lsp_refresh[level - 1]);
        area->lsp_regenerate_pending[level - 1] = 0;
-       if (level == IS_LEVEL_1)
-               thread_add_timer(master, lsp_l1_refresh, area, refresh_time,
-                                &area->t_lsp_refresh[level - 1]);
-       else if (level == IS_LEVEL_2)
-               thread_add_timer(master, lsp_l2_refresh, area, refresh_time,
-                                &area->t_lsp_refresh[level - 1]);
+       thread_add_timer(master, lsp_refresh,
+                        &area->lsp_refresh_arg[level - 1], refresh_time,
+                        &area->t_lsp_refresh[level - 1]);
 
        if (isis->debugs & DEBUG_UPDATE_PACKETS) {
                zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %" PRIu16
@@ -1253,6 +1274,12 @@ int lsp_generate(struct isis_area *area, int level)
                "ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.",
                area->area_tag, level);
 
+#ifndef FABRICD
+       /* send northbound notification */
+       isis_notif_lsp_gen(area, rawlspid_print(newlsp->hdr.lsp_id),
+                          newlsp->hdr.seqno, newlsp->last_generated);
+#endif /* ifndef FABRICD */
+
        return ISIS_OK;
 }
 
@@ -1290,7 +1317,15 @@ static int lsp_regenerate(struct isis_area *area, int level)
        lsp->hdr.rem_lifetime = rem_lifetime;
        lsp->last_generated = time(NULL);
        lsp_flood(lsp, NULL);
+       area->lsp_gen_count[level - 1]++;
        for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) {
+               if (!frag->tlvs) {
+                       /* Updating and flooding should only affect fragments
+                        * carrying data
+                        */
+                       continue;
+               }
+
                frag->hdr.lsp_bits = lsp_bits_generate(
                        level, area->overload_bit, area->attached_bit);
                /* Set the lifetime values of all the fragments to the same
@@ -1304,12 +1339,9 @@ static int lsp_regenerate(struct isis_area *area, int level)
        lsp_seqno_update(lsp);
 
        refresh_time = lsp_refresh_time(lsp, rem_lifetime);
-       if (level == IS_LEVEL_1)
-               thread_add_timer(master, lsp_l1_refresh, area, refresh_time,
-                                &area->t_lsp_refresh[level - 1]);
-       else if (level == IS_LEVEL_2)
-               thread_add_timer(master, lsp_l2_refresh, area, refresh_time,
-                                &area->t_lsp_refresh[level - 1]);
+       thread_add_timer(master, lsp_refresh,
+                        &area->lsp_refresh_arg[level - 1], refresh_time,
+                        &area->t_lsp_refresh[level - 1]);
        area->lsp_regenerate_pending[level - 1] = 0;
 
        if (isis->debugs & DEBUG_UPDATE_PACKETS) {
@@ -1331,45 +1363,42 @@ static int lsp_regenerate(struct isis_area *area, int level)
 /*
  * Something has changed or periodic refresh -> regenerate LSP
  */
-static int lsp_l1_refresh(struct thread *thread)
+static int lsp_refresh(struct thread *thread)
 {
-       struct isis_area *area;
+       struct lsp_refresh_arg *arg = THREAD_ARG(thread);
 
-       area = THREAD_ARG(thread);
-       assert(area);
-
-       area->t_lsp_refresh[0] = NULL;
-       area->lsp_regenerate_pending[0] = 0;
+       assert(arg);
 
-       if ((area->is_type & IS_LEVEL_1) == 0)
-               return ISIS_ERROR;
+       struct isis_area *area = arg->area;
 
-       sched_debug(
-               "ISIS (%s): LSP L1 refresh timer expired. Refreshing LSP...",
-               area->area_tag);
-       return lsp_regenerate(area, IS_LEVEL_1);
-}
-
-static int lsp_l2_refresh(struct thread *thread)
-{
-       struct isis_area *area;
-
-       area = THREAD_ARG(thread);
        assert(area);
 
-       area->t_lsp_refresh[1] = NULL;
-       area->lsp_regenerate_pending[1] = 0;
+       int level = arg->level;
+
+       area->t_lsp_refresh[level - 1] = NULL;
+       area->lsp_regenerate_pending[level - 1] = 0;
 
-       if ((area->is_type & IS_LEVEL_2) == 0)
+       if ((area->is_type & level) == 0)
                return ISIS_ERROR;
 
+       if (monotime_since(&area->last_lsp_refresh_event[level - 1], NULL) < 100000L) {
+               sched_debug("ISIS (%s): Still unstable, postpone LSP L%d refresh",
+                           area->area_tag, level);
+               _lsp_regenerate_schedule(area, level, 0, false,
+                                        __func__, __FILE__, __LINE__);
+               return 0;
+       }
+
        sched_debug(
-               "ISIS (%s): LSP L2 refresh timer expired. Refreshing LSP...",
-               area->area_tag);
-       return lsp_regenerate(area, IS_LEVEL_2);
+               "ISIS (%s): LSP L%d refresh timer expired. Refreshing LSP...",
+               area->area_tag, level);
+       return lsp_regenerate(area, level);
 }
 
-int lsp_regenerate_schedule(struct isis_area *area, int level, int all_pseudo)
+int _lsp_regenerate_schedule(struct isis_area *area, int level,
+                            int all_pseudo, bool postpone,
+                            const char *func, const char *file,
+                            int line)
 {
        struct isis_lsp *lsp;
        uint8_t id[ISIS_SYS_ID_LEN + 2];
@@ -1383,9 +1412,11 @@ int lsp_regenerate_schedule(struct isis_area *area, int level, int all_pseudo)
                return ISIS_ERROR;
 
        sched_debug(
-               "ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs",
+               "ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs"
+               " Caller: %s %s:%d",
                area->area_tag, circuit_t2string(level),
-               all_pseudo ? "" : "not ");
+               all_pseudo ? "" : "not ",
+               func, file, line);
 
        memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
        LSP_PSEUDO_ID(id) = LSP_FRAGMENT(id) = 0;
@@ -1395,6 +1426,10 @@ int lsp_regenerate_schedule(struct isis_area *area, int level, int all_pseudo)
                if (!((level & lvl) && (area->is_type & lvl)))
                        continue;
 
+               if (postpone) {
+                       monotime(&area->last_lsp_refresh_event[lvl - 1]);
+               }
+
                sched_debug(
                        "ISIS (%s): Checking whether L%d needs to be scheduled",
                        area->area_tag, lvl);
@@ -1449,15 +1484,10 @@ int lsp_regenerate_schedule(struct isis_area *area, int level, int all_pseudo)
                }
 
                area->lsp_regenerate_pending[lvl - 1] = 1;
-               if (lvl == IS_LEVEL_1) {
-                       thread_add_timer_msec(master, lsp_l1_refresh, area,
-                                             timeout,
-                                             &area->t_lsp_refresh[lvl - 1]);
-               } else if (lvl == IS_LEVEL_2) {
-                       thread_add_timer_msec(master, lsp_l2_refresh, area,
-                                             timeout,
-                                             &area->t_lsp_refresh[lvl - 1]);
-               }
+               thread_add_timer_msec(master, lsp_refresh,
+                                     &area->lsp_refresh_arg[lvl - 1],
+                                     timeout,
+                                     &area->t_lsp_refresh[lvl - 1]);
        }
 
        if (all_pseudo) {
@@ -1950,8 +1980,9 @@ void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr,
        lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp));
        lsp->area = area;
        lsp->level = level;
-       lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu);
+       lsp_adjust_stream(lsp);
        lsp->age_out = ZERO_AGE_LIFETIME;
+       lsp->area->lsp_purge_count[level - 1]++;
 
        memcpy(&lsp->hdr, hdr, sizeof(lsp->hdr));
        lsp->hdr.rem_lifetime = 0;
@@ -1987,15 +2018,24 @@ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set)
        }
 }
 
-void lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit)
+void _lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit,
+               const char *func, const char *file, int line)
 {
-       if (!fabricd) {
-               lsp_set_all_srmflags(lsp, true);
-               if (circuit)
-                       isis_tx_queue_del(circuit->tx_queue, lsp);
-       } else {
-               fabricd_lsp_flood(lsp);
+       if (isis->debugs & DEBUG_FLOODING) {
+               zlog_debug("Flooding LSP %s%s%s (From %s %s:%d)",
+                          rawlspid_print(lsp->hdr.lsp_id),
+                          circuit ? " except on " : "",
+                          circuit ? circuit->interface->name : "",
+                          func, file, line);
        }
+
+       if (!fabricd)
+               lsp_set_all_srmflags(lsp, true);
+       else
+               fabricd_lsp_flood(lsp, circuit);
+
+       if (circuit)
+               isis_tx_queue_del(circuit->tx_queue, lsp);
 }
 
 static int lsp_handle_adj_state_change(struct isis_adjacency *adj)