]> git.puffer.fish Git - matthieu/frr.git/commitdiff
isisd: announce and parse MT IS reachabilities
authorChristian Franke <chris@opensourcerouting.org>
Thu, 27 Apr 2017 11:56:43 +0000 (13:56 +0200)
committerChristian Franke <chris@opensourcerouting.org>
Fri, 28 Apr 2017 10:03:23 +0000 (12:03 +0200)
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
isisd/isis_adjacency.c
isisd/isis_lsp.c
isisd/isis_mt.c
isisd/isis_mt.h
isisd/isis_tlv.c
isisd/isis_tlv.h

index c6fc6b008d2c4e881487efca54e29b0d612d70d3..fea99ec90776f1472a12cff6a9451148a3a135f2 100644 (file)
@@ -47,6 +47,7 @@
 #include "isisd/isis_lsp.h"
 #include "isisd/isis_spf.h"
 #include "isisd/isis_events.h"
+#include "isisd/isis_mt.h"
 
 extern struct isis *isis;
 
index d7d76942a6648d3b925c4c798f0dcdd9c628f8b9..5009c34b02def771d2d771fa138afd928dfb9104 100644 (file)
@@ -828,6 +828,34 @@ lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost)
            lsp_bits2string (&lsp->lsp_header->lsp_bits), VTY_NEWLINE);
 }
 
+static void
+lsp_print_mt_reach(struct list *list, struct vty *vty,
+                   char dynhost, uint16_t mtid)
+{
+  struct listnode *node;
+  struct te_is_neigh *neigh;
+
+  for (ALL_LIST_ELEMENTS_RO (list, node, neigh))
+    {
+      u_char lspid[255];
+
+      lspid_print(neigh->neigh_id, lspid, dynhost, 0);
+      if (mtid == ISIS_MT_IPV4_UNICAST)
+        {
+          vty_out(vty, "  Metric      : %-8d IS-Extended   : %s%s",
+                  GET_TE_METRIC(neigh), lspid, VTY_NEWLINE);
+        }
+      else
+        {
+          vty_out(vty, "  Metric      : %-8d MT-Reach      : %s %s%s",
+                  GET_TE_METRIC(neigh), lspid,
+                  isis_mtid2str(mtid), VTY_NEWLINE);
+        }
+      if (IS_MPLS_TE(isisMplsTE))
+        mpls_te_print_detail(vty, neigh);
+    }
+}
+
 void
 lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost)
 {
@@ -835,12 +863,12 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost)
   int i;
   struct listnode *lnode;
   struct is_neigh *is_neigh;
-  struct te_is_neigh *te_is_neigh;
   struct ipv4_reachability *ipv4_reach;
   struct in_addr *ipv4_addr;
   struct te_ipv4_reachability *te_ipv4_reach;
   struct ipv6_reachability *ipv6_reach;
   struct mt_router_info *mt_router_info;
+  struct tlv_mt_neighbors *mt_is_neigh;
   struct in6_addr in6;
   u_char buff[BUFSIZ];
   u_char LSPid[255];
@@ -978,15 +1006,12 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost)
     }
 
   /* TE IS neighbor tlv */
-  if (lsp->tlv_data.te_is_neighs)
-    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, lnode, te_is_neigh))
-    {
-      lspid_print (te_is_neigh->neigh_id, LSPid, dynhost, 0);
-      vty_out (vty, "  Metric      : %-8d IS-Extended   : %s%s",
-              GET_TE_METRIC(te_is_neigh), LSPid, VTY_NEWLINE);
-      if (IS_MPLS_TE(isisMplsTE))
-        mpls_te_print_detail(vty, te_is_neigh);
-    }
+  lsp_print_mt_reach(lsp->tlv_data.te_is_neighs, vty,
+                     dynhost, ISIS_MT_IPV4_UNICAST);
+
+  /* MT IS neighbor tlv */
+  for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_is_neighs, lnode, mt_is_neigh))
+    lsp_print_mt_reach(mt_is_neigh->list, vty, dynhost, mt_is_neigh->mtid);
 
   /* TE IPv4 tlv */
   if (lsp->tlv_data.te_ipv4_reachs)
@@ -1039,6 +1064,42 @@ lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost)
   return lsp_count;
 }
 
+static void
+_lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to,
+              int frag_thold,
+              unsigned int tlv_build_func (struct list *, struct stream *,
+                                           void *arg),
+              void *arg)
+{
+  while (*from && listcount(*from))
+    {
+      unsigned int count;
+
+      count = tlv_build_func(*from, lsp->pdu, arg);
+
+      if (listcount(*to) != 0 || count != listcount(*from))
+        {
+          struct listnode *node, *nnode;
+          void *elem;
+
+          for (ALL_LIST_ELEMENTS(*from, node, nnode, elem))
+            {
+              if (!count)
+                break;
+              listnode_add (*to, elem);
+              list_delete_node (*from, node);
+              --count;
+            }
+        }
+      else
+        {
+          list_free (*to);
+          *to = *from;
+          *from = NULL;
+        }
+    }
+}
+
 #define FRAG_THOLD(S,T) \
   ((STREAM_SIZE(S)*T)/100)
 
@@ -1637,10 +1698,8 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area)
                         /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */
                         te_is_neigh->sub_tlvs_length = 0;
 
-                      listnode_add (tlv_data.te_is_neighs, te_is_neigh);
-                      lsp_debug("ISIS (%s): Adding DIS %s.%02x as te-style neighbor",
-                                area->area_tag, sysid_print(te_is_neigh->neigh_id),
-                                LSP_PSEUDO_ID(te_is_neigh->neigh_id));
+                      tlvs_add_mt_bcast(&tlv_data, circuit, level, te_is_neigh);
+                      XFREE(MTYPE_ISIS_TLV, te_is_neigh);
                     }
                }
            }
@@ -1697,9 +1756,9 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area)
                   else
                     /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */
                     te_is_neigh->sub_tlvs_length = 0;
-                 listnode_add (tlv_data.te_is_neighs, te_is_neigh);
-                 lsp_debug("ISIS (%s): Adding te-style is reach for %s", area->area_tag,
-                            sysid_print(te_is_neigh->neigh_id));
+
+                  tlvs_add_mt_p2p(&tlv_data, circuit, te_is_neigh);
+                  XFREE(MTYPE_ISIS_TLV, te_is_neigh);
                }
            }
           else
@@ -1791,13 +1850,31 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area)
     {
       if (lsp->tlv_data.te_is_neighs == NULL)
        lsp->tlv_data.te_is_neighs = list_new ();
-      lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs,
-                  IS_NEIGHBOURS_LEN, area->lsp_frag_threshold,
-                  tlv_add_te_is_neighs);
+      _lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs,
+                   area->lsp_frag_threshold, tlv_add_te_is_neighs, NULL);
       if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs))
        lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
                             lsp0, area, level);
     }
+
+  struct tlv_mt_neighbors *mt_neighs;
+  for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_is_neighs, node, mt_neighs))
+    {
+      while (mt_neighs->list && listcount(mt_neighs->list))
+        {
+          struct tlv_mt_neighbors *frag_mt_neighs;
+
+          frag_mt_neighs = tlvs_get_mt_neighbors(&lsp->tlv_data, mt_neighs->mtid);
+          _lsp_tlv_fit (lsp, &mt_neighs->list, &frag_mt_neighs->list,
+                        area->lsp_frag_threshold, tlv_add_te_is_neighs,
+                        &mt_neighs->mtid);
+          if (mt_neighs->list && listcount(mt_neighs->list))
+            lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                                 lsp0, area, level);
+        }
+    }
+
+
   lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
 
   free_tlvs (&tlv_data);
@@ -2234,7 +2311,7 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit,
     tlv_add_is_neighs (lsp->tlv_data.is_neighs, lsp->pdu);
 
   if (lsp->tlv_data.te_is_neighs && listcount (lsp->tlv_data.te_is_neighs) > 0)
-    tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu);
+    tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu, NULL);
 
   if (lsp->tlv_data.es_neighs && listcount (lsp->tlv_data.es_neighs) > 0)
     tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu);
index 4e36b914333aa338a9cc7f960d66969b899ddc3b..e66c9d7d9c4febaa2ef33647eccbe66c1bd9b26a 100644 (file)
 #include "isisd/isis_circuit.h"
 #include "isisd/isis_adjacency.h"
 #include "isisd/isis_tlv.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_lsp.h"
 #include "isisd/isis_mt.h"
 
 DEFINE_MTYPE_STATIC(ISISD, MT_AREA_SETTING, "ISIS MT Area Setting")
 DEFINE_MTYPE_STATIC(ISISD, MT_CIRCUIT_SETTING, "ISIS MT Circuit Setting")
 DEFINE_MTYPE_STATIC(ISISD, MT_ADJ_INFO, "ISIS MT Adjacency Info")
+DEFINE_MTYPE_STATIC(ISISD, MT_NEIGHBORS, "ISIS MT Neighbors for TLV")
 
 /* MT naming api */
 const char *isis_mtid2str(uint16_t mtid)
@@ -362,6 +365,7 @@ circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count)
   return rv;
 }
 
+/* ADJ specific MT API */
 static void adj_mt_set(struct isis_adjacency *adj, unsigned int index,
                        uint16_t mtid)
 {
@@ -437,3 +441,156 @@ adj_mt_finish(struct isis_adjacency *adj)
   XFREE(MTYPE_MT_ADJ_INFO, adj->mt_set);
   adj->mt_count = 0;
 }
+
+/* TLV MT Neighbors api */
+struct tlv_mt_neighbors*
+tlvs_lookup_mt_neighbors(struct tlvs *tlvs, uint16_t mtid)
+{
+  return lookup_mt_setting(tlvs->mt_is_neighs, mtid);
+}
+
+static struct tlv_mt_neighbors*
+tlvs_new_mt_neighbors(uint16_t mtid)
+{
+  struct tlv_mt_neighbors *rv;
+
+  rv = XCALLOC(MTYPE_MT_NEIGHBORS, sizeof(*rv));
+  rv->mtid = mtid;
+  rv->list = list_new();
+
+  return rv;
+};
+
+static void
+tlvs_free_mt_neighbors(void *arg)
+{
+  struct tlv_mt_neighbors *neighbors = arg;
+
+  if (neighbors && neighbors->list)
+    list_delete(neighbors->list);
+  XFREE(MTYPE_MT_NEIGHBORS, neighbors);
+}
+
+static void
+tlvs_add_mt_neighbors(struct tlvs *tlvs, struct tlv_mt_neighbors *neighbors)
+{
+  add_mt_setting(&tlvs->mt_is_neighs, neighbors);
+  tlvs->mt_is_neighs->del = tlvs_free_mt_neighbors;
+}
+
+struct tlv_mt_neighbors*
+tlvs_get_mt_neighbors(struct tlvs *tlvs, uint16_t mtid)
+{
+  struct tlv_mt_neighbors *neighbors;
+
+  neighbors = tlvs_lookup_mt_neighbors(tlvs, mtid);
+  if (!neighbors)
+    {
+      neighbors = tlvs_new_mt_neighbors(mtid);
+      tlvs_add_mt_neighbors(tlvs, neighbors);
+    }
+  return neighbors;
+}
+
+static void
+mt_set_add(uint16_t **mt_set, unsigned int *size,
+           unsigned int *index, uint16_t mtid)
+{
+  for (unsigned int i = 0; i < *index; i++)
+    {
+      if ((*mt_set)[i] == mtid)
+        return;
+    }
+
+  if (*index >= *size)
+    {
+      *mt_set = XREALLOC(MTYPE_TMP, *mt_set, sizeof(**mt_set) * ((*index) + 1));
+      *size = (*index) + 1;
+    }
+
+  (*mt_set)[*index] = mtid;
+  *index = (*index) + 1;
+}
+
+static uint16_t *
+circuit_bcast_mt_set(struct isis_circuit *circuit, int level,
+                     unsigned int *mt_count)
+{
+  static uint16_t *rv;
+  static unsigned int size;
+  struct listnode *node;
+  struct isis_adjacency *adj;
+
+  unsigned int count = 0;
+
+  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+    {
+      *mt_count = 0;
+      return NULL;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[level - 1], node, adj))
+    {
+      if (adj->adj_state != ISIS_ADJ_UP)
+        continue;
+      for (unsigned int i = 0; i < adj->mt_count; i++)
+        mt_set_add(&rv, &size, &count, adj->mt_set[i]);
+    }
+
+  *mt_count = count;
+  return rv;
+}
+
+static void
+tlvs_add_mt_set(struct isis_area *area,
+                struct tlvs *tlvs, unsigned int mt_count,
+                uint16_t *mt_set, struct te_is_neigh *neigh)
+{
+  for (unsigned int i = 0; i < mt_count; i++)
+    {
+      uint16_t mtid = mt_set[i];
+      struct te_is_neigh *ne_copy;
+
+      ne_copy = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ne_copy));
+      memcpy(ne_copy, neigh, sizeof(*ne_copy));
+
+      if (mt_set[i] == ISIS_MT_IPV4_UNICAST)
+        {
+          listnode_add(tlvs->te_is_neighs, ne_copy);
+          lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor",
+                    area->area_tag, sysid_print(ne_copy->neigh_id),
+                    LSP_PSEUDO_ID(ne_copy->neigh_id));
+        }
+      else
+        {
+          struct tlv_mt_neighbors *neighbors;
+
+          neighbors = tlvs_get_mt_neighbors(tlvs, mtid);
+          neighbors->list->del = free_tlv;
+          listnode_add(neighbors->list, ne_copy);
+          lsp_debug("ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
+                    area->area_tag, sysid_print(ne_copy->neigh_id),
+                    LSP_PSEUDO_ID(ne_copy->neigh_id), isis_mtid2str(mtid));
+        }
+    }
+}
+
+void
+tlvs_add_mt_bcast(struct tlvs *tlvs, struct isis_circuit *circuit,
+                  int level, struct te_is_neigh *neigh)
+{
+  unsigned int mt_count;
+  uint16_t *mt_set = circuit_bcast_mt_set(circuit, level,
+                                          &mt_count);
+
+  tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, neigh);
+}
+
+void
+tlvs_add_mt_p2p(struct tlvs *tlvs, struct isis_circuit *circuit,
+                struct te_is_neigh *neigh)
+{
+  struct isis_adjacency *adj = circuit->u.p2p.neighbor;
+
+  tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, neigh);
+}
index 3ad8c05e4710ff3ebccb0c9fadd23e0c353806f8..313e992b4a17db7c04711134c4e236fd2ea76cc0 100644 (file)
@@ -53,6 +53,8 @@
 #define ISIS_MT_INFO_FIELDS \
   uint16_t mtid;
 
+struct list;
+
 struct isis_area_mt_setting {
   ISIS_MT_INFO_FIELDS
   bool enabled;
@@ -64,6 +66,11 @@ struct isis_circuit_mt_setting {
   bool enabled;
 };
 
+struct tlv_mt_neighbors {
+  ISIS_MT_INFO_FIELDS
+  struct list *list;
+};
+
 const char *isis_mtid2str(uint16_t mtid);
 uint16_t isis_str2mtid(const char *name);
 
@@ -71,6 +78,10 @@ struct isis_adjacency;
 struct isis_area;
 struct isis_circuit;
 struct tlvs;
+struct te_is_neigh;
+
+struct tlv_mt_neighbors* tlvs_lookup_mt_neighbors(struct tlvs *tlvs, uint16_t mtid);
+struct tlv_mt_neighbors* tlvs_get_mt_neighbors(struct tlvs *tlvs, uint16_t mtid);
 
 struct isis_area_mt_setting* area_lookup_mt_setting(struct isis_area *area,
                                                     uint16_t mtid);
@@ -107,4 +118,8 @@ struct isis_circuit_mt_setting** circuit_mt_settings(struct isis_circuit *circui
 bool tlvs_to_adj_mt_set(struct tlvs *tlvs, bool v4_usable, bool v6_usable,
                         struct isis_adjacency *adj);
 void adj_mt_finish(struct isis_adjacency *adj);
+void tlvs_add_mt_bcast(struct tlvs *tlvs, struct isis_circuit *circuit,
+                       int level, struct te_is_neigh *neigh);
+void tlvs_add_mt_p2p(struct tlvs *tlvs, struct isis_circuit *circuit,
+                     struct te_is_neigh *neigh);
 #endif
index 14ebed94d31eb94216b9170929e16340e47201c4..41c861bf58fee74e86a44430a115a22072f013d5 100644 (file)
@@ -43,6 +43,7 @@
 #include "isisd/isis_pdu.h"
 #include "isisd/isis_lsp.h"
 #include "isisd/isis_te.h"
+#include "isisd/isis_mt.h"
 
 void
 free_tlv (void *val)
@@ -67,6 +68,8 @@ free_tlvs (struct tlvs *tlvs)
     list_delete (tlvs->is_neighs);
   if (tlvs->te_is_neighs)
     list_delete (tlvs->te_is_neighs);
+  if (tlvs->mt_is_neighs)
+    list_delete (tlvs->mt_is_neighs);
   if (tlvs->es_neighs)
     list_delete (tlvs->es_neighs);
   if (tlvs->lsp_entries)
@@ -93,6 +96,83 @@ free_tlvs (struct tlvs *tlvs)
   return;
 }
 
+static int
+parse_mt_is_neighs(struct tlvs *tlvs, bool read_mtid,
+                   unsigned int length, u_char *pnt)
+{
+  struct list *neigh_list;
+  uint16_t mtid;
+
+  if (read_mtid)
+    {
+      uint16_t mtid_buf;
+
+      if (length < sizeof(mtid_buf))
+        {
+          zlog_warn("ISIS-TLV: mt tlv too short to contain MT id");
+          return ISIS_WARNING;
+        }
+
+      memcpy(&mtid_buf, pnt, sizeof(mtid_buf));
+      pnt += sizeof(mtid_buf);
+      length -= sizeof(mtid_buf);
+
+      mtid = ntohs(mtid_buf) & ISIS_MT_MASK;
+    }
+  else
+    {
+      mtid = ISIS_MT_IPV4_UNICAST;
+    }
+
+  if (mtid == ISIS_MT_IPV4_UNICAST)
+    {
+      if (!tlvs->te_is_neighs)
+        {
+          tlvs->te_is_neighs = list_new();
+          tlvs->te_is_neighs->del = free_tlv;
+        }
+      neigh_list = tlvs->te_is_neighs;
+    }
+  else
+    {
+      struct tlv_mt_neighbors *neighbors;
+
+      neighbors = tlvs_get_mt_neighbors(tlvs, mtid);
+      neighbors->list->del = free_tlv;
+      neigh_list = neighbors->list;
+    }
+
+  while (length >= IS_NEIGHBOURS_LEN)
+    {
+      struct te_is_neigh *neigh = XCALLOC(MTYPE_ISIS_TLV, sizeof(*neigh));
+
+      memcpy(neigh, pnt, IS_NEIGHBOURS_LEN);
+      pnt += IS_NEIGHBOURS_LEN;
+      length -= IS_NEIGHBOURS_LEN;
+
+      if (neigh->sub_tlvs_length > length)
+        {
+          zlog_warn("ISIS-TLV: neighbor subtlv length exceeds TLV size");
+          XFREE(MTYPE_ISIS_TLV, neigh);
+          return ISIS_WARNING;
+        }
+
+      memcpy(neigh->sub_tlvs, pnt, neigh->sub_tlvs_length);
+      pnt += neigh->sub_tlvs_length;
+      length -= neigh->sub_tlvs_length;
+
+      listnode_add(neigh_list, neigh);
+    }
+
+  if (length)
+    {
+      zlog_warn("ISIS-TLV: TE/MT neighor TLV has trailing data");
+      return ISIS_WARNING;
+    }
+
+  return ISIS_OK;
+}
+
 /*
  * Parses the tlvs found in the variant length part of the PDU.
  * Caller tells with flags in "expected" which TLV's it is interested in.
@@ -105,7 +185,6 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
   struct lan_neigh *lan_nei;
   struct area_addr *area_addr;
   struct is_neigh *is_nei;
-  struct te_is_neigh *te_is_nei;
   struct es_neigh *es_nei;
   struct lsp_entry *lsp_entry;
   struct in_addr *ipv4_addr;
@@ -209,54 +288,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
          break;
 
        case TE_IS_NEIGHBOURS:
-         /* +-------+-------+-------+-------+-------+-------+-------+-------+
-          * |                        Neighbour ID                           | 7
-          * +---------------------------------------------------------------+
-          * |                        TE Metric                              | 3
-          * +---------------------------------------------------------------+
-          * |                        SubTLVs Length                         | 1
-          * +---------------------------------------------------------------+
-          * :                                                               :
-          */
          *found |= TLVFLAG_TE_IS_NEIGHS;
 #ifdef EXTREME_TLV_DEBUG
          zlog_debug ("ISIS-TLV (%s): Extended IS Neighbours length %d",
                     areatag, length);
 #endif /* EXTREME_TLV_DEBUG */
          if (TLVFLAG_TE_IS_NEIGHS & *expected)
-           {
-             while (length > value_len)
-               {
-                 te_is_nei = (struct te_is_neigh *) pnt;
-                 value_len += IS_NEIGHBOURS_LEN;
-                 pnt += IS_NEIGHBOURS_LEN;
-                  /* FIXME - subtlvs are handled here, for now we skip */
-                 /* FIXME: All TE SubTLVs are not necessary present in LSP PDU. */
-                 /* So, it must be copied in a new te_is_neigh structure        */
-                 /* rather than just initialize pointer to the original LSP PDU */
-                 /* to avoid consider the rest of lspdu as subTLVs or buffer overflow */
-                 if (IS_MPLS_TE(isisMplsTE))
-                   {
-                     struct te_is_neigh *new = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct te_is_neigh));
-                     memcpy(new->neigh_id, te_is_nei->neigh_id, ISIS_SYS_ID_LEN + 1);
-                     memcpy(new->te_metric, te_is_nei->te_metric, 3);
-                     new->sub_tlvs_length = te_is_nei->sub_tlvs_length;
-                     memcpy(new->sub_tlvs, pnt, te_is_nei->sub_tlvs_length);
-                      te_is_nei = new;
-                    }
-                 /* Skip SUB TLVs payload */
-                 value_len += te_is_nei->sub_tlvs_length;
-                 pnt += te_is_nei->sub_tlvs_length;
-
-                 if (!tlvs->te_is_neighs)
-                   tlvs->te_is_neighs = list_new ();
-                 listnode_add (tlvs->te_is_neighs, te_is_nei);
-               }
-           }
-         else
-           {
-             pnt += length;
-           }
+           retval = parse_mt_is_neighs(tlvs, false, length, pnt);
+         pnt += length;
+         break;
+
+       case MT_IS_NEIGHBOURS:
+         *found |= TLVFLAG_TE_IS_NEIGHS;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): MT IS Neighbours length %d",
+                     areatag, length);
+#endif
+         if (TLVFLAG_TE_IS_NEIGHS & *expected)
+           retval = parse_mt_is_neighs(tlvs, true, length, pnt);
+         pnt += length;
          break;
 
        case ES_NEIGHBOURS:
@@ -950,26 +1000,44 @@ tlv_add_is_neighs (struct list *is_neighs, struct stream *stream)
   return add_tlv (IS_NEIGHBOURS, pos - value, value, stream);
 }
 
-int
-tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream)
+static size_t
+max_tlv_size(struct stream *stream)
+{
+  size_t avail = stream_get_size (stream) - stream_get_endp(stream);
+
+  if (avail < 2)
+    return 0;
+
+  if (avail < 257)
+    return avail - 2;
+
+  return 255;
+}
+
+unsigned int
+tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream, void *arg)
 {
   struct listnode *node;
   struct te_is_neigh *te_is_neigh;
   u_char value[255];
   u_char *pos = value;
-  int retval;
+  uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST;
+  unsigned int consumed = 0;
+  size_t max_size = max_tlv_size(stream);
+
+  if (mtid != ISIS_MT_IPV4_UNICAST)
+    {
+      uint16_t mtid_conversion = ntohs(mtid);
+      memcpy(pos, &mtid_conversion, sizeof(mtid_conversion));
+      pos += sizeof(mtid_conversion);
+    }
 
   for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh))
     {
       /* FIXME: Check if Total SubTLVs size doesn't exceed 255 */
-      if (pos - value + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > 255)
-        {
-          retval = add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream);
-          if (retval != ISIS_OK)
-            return retval;
-          pos = value;
-        }
-      
+      if ((size_t)(pos - value) + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > max_size)
+        break;
+
       memcpy (pos, te_is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1);
       pos += ISIS_SYS_ID_LEN + 1;
       memcpy (pos, te_is_neigh->te_metric, 3);
@@ -983,9 +1051,17 @@ tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream)
           memcpy (pos, te_is_neigh->sub_tlvs, te_is_neigh->sub_tlvs_length);
           pos += te_is_neigh->sub_tlvs_length;
         }
+      consumed++;
     }
 
-  return add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream);
+  if (consumed)
+    {
+      int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IS_NEIGHBOURS
+                                                       : TE_IS_NEIGHBOURS,
+                        pos - value, value, stream);
+      assert(rv == ISIS_OK);
+    }
+  return consumed;
 }
 
 int
index 12025ff73a799585490cf954cf7d385498550eae..f3c04baf54e34ef3fc8789d9635c01b36ad5db6b 100644 (file)
 #define TE_IPV4_REACHABILITY      135
 #define DYNAMIC_HOSTNAME          137
 #define GRACEFUL_RESTART          211
+#define MT_IS_NEIGHBOURS          222
 #define MT_ROUTER_INFORMATION     229
 #define IPV6_ADDR                 232
 #define IPV6_REACHABILITY         236
@@ -272,6 +273,7 @@ struct tlvs
   struct list *mt_router_info;
   struct list *is_neighs;
   struct list *te_is_neighs;
+  struct list *mt_is_neighs;
   struct list *es_neighs;
   struct list *lsp_entries;
   struct list *prefix_neighs;
@@ -324,7 +326,7 @@ void free_tlv (void *val);
 int tlv_add_mt_router_info (struct list *mt_router_info, struct stream *stream);
 int tlv_add_area_addrs (struct list *area_addrs, struct stream *stream);
 int tlv_add_is_neighs (struct list *is_neighs, struct stream *stream);
-int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream);
+unsigned int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream, void *arg);
 int tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream);
 int tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream);
 int tlv_add_checksum (struct checksum *checksum, struct stream *stream);