]> git.puffer.fish Git - matthieu/frr.git/commitdiff
isisd: announce and parse MT IP reachabilities
authorChristian Franke <chris@opensourcerouting.org>
Thu, 27 Apr 2017 11:56:45 +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_lsp.c
isisd/isis_mt.c
isisd/isis_mt.h
isisd/isis_tlv.c
isisd/isis_tlv.h

index 5009c34b02def771d2d771fa138afd928dfb9104..955a73ef6128e169719cd4e17aafbb5ebb55af74 100644 (file)
@@ -856,6 +856,79 @@ lsp_print_mt_reach(struct list *list, struct vty *vty,
     }
 }
 
+static void
+lsp_print_mt_ipv6_reach(struct list *list, struct vty *vty, uint16_t mtid)
+{
+  struct listnode *node;
+  struct ipv6_reachability *ipv6_reach;
+  struct in6_addr in6;
+  u_char buff[BUFSIZ];
+
+  for (ALL_LIST_ELEMENTS_RO (list, node, ipv6_reach))
+    {
+      memset (&in6, 0, sizeof (in6));
+      memcpy (in6.s6_addr, ipv6_reach->prefix,
+              PSIZE (ipv6_reach->prefix_len));
+      inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ);
+      if (mtid == ISIS_MT_IPV4_UNICAST)
+        {
+          if ((ipv6_reach->control_info &
+               CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL)
+            vty_out (vty, "  Metric      : %-8d IPv6-Internal : %s/%d%s",
+                     ntohl (ipv6_reach->metric),
+                     buff, ipv6_reach->prefix_len, VTY_NEWLINE);
+          else
+            vty_out (vty, "  Metric      : %-8d IPv6-External : %s/%d%s",
+                     ntohl (ipv6_reach->metric),
+                     buff, ipv6_reach->prefix_len, VTY_NEWLINE);
+        }
+      else
+        {
+          if ((ipv6_reach->control_info &
+               CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL)
+            vty_out (vty, "  Metric      : %-8d IPv6-MT-Int   : %s/%d %s%s",
+                     ntohl (ipv6_reach->metric),
+                     buff, ipv6_reach->prefix_len,
+                     isis_mtid2str(mtid), VTY_NEWLINE);
+          else
+            vty_out (vty, "  Metric      : %-8d IPv6-MT-Ext   : %s/%d %s%s",
+                     ntohl (ipv6_reach->metric),
+                     buff, ipv6_reach->prefix_len,
+                     isis_mtid2str(mtid), VTY_NEWLINE);
+        }
+    }
+}
+
+static void
+lsp_print_mt_ipv4_reach(struct list *list, struct vty *vty, uint16_t mtid)
+{
+  struct listnode *node;
+  struct te_ipv4_reachability *te_ipv4_reach;
+
+  for (ALL_LIST_ELEMENTS_RO (list, node, te_ipv4_reach))
+    {
+      if (mtid == ISIS_MT_IPV4_UNICAST)
+        {
+          /* FIXME: There should be better way to output this stuff. */
+          vty_out (vty, "  Metric      : %-8d IPv4-Extended : %s/%d%s",
+                   ntohl (te_ipv4_reach->te_metric),
+                   inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start,
+                                                te_ipv4_reach->control)),
+                   te_ipv4_reach->control & 0x3F, VTY_NEWLINE);
+        }
+      else
+        {
+          /* FIXME: There should be better way to output this stuff. */
+          vty_out (vty, "  Metric      : %-8d IPv4-MT       : %s/%d %s%s",
+                   ntohl (te_ipv4_reach->te_metric),
+                   inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start,
+                                                te_ipv4_reach->control)),
+                   te_ipv4_reach->control & 0x3F,
+                   isis_mtid2str(mtid), VTY_NEWLINE);
+        }
+    }
+}
+
 void
 lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost)
 {
@@ -865,12 +938,10 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost)
   struct is_neigh *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_ipv6_reachs *mt_ipv6_reachs;
   struct tlv_mt_neighbors *mt_is_neigh;
-  struct in6_addr in6;
-  u_char buff[BUFSIZ];
+  struct tlv_mt_ipv4_reachs *mt_ipv4_reachs;
   u_char LSPid[255];
   u_char hostname[255];
   u_char ipv4_reach_prefix[20];
@@ -985,25 +1056,14 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost)
               ipv4_reach->metrics.metric_default, ipv4_reach_prefix,
               ipv4_reach_mask, VTY_NEWLINE);
     }
-  
+
   /* IPv6 tlv */
-  if (lsp->tlv_data.ipv6_reachs)
-    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, lnode, ipv6_reach))
-    {
-      memset (&in6, 0, sizeof (in6));
-      memcpy (in6.s6_addr, ipv6_reach->prefix,
-             PSIZE (ipv6_reach->prefix_len));
-      inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ);
-      if ((ipv6_reach->control_info &
-          CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL)
-       vty_out (vty, "  Metric      : %-8d IPv6-Internal : %s/%d%s",
-                ntohl (ipv6_reach->metric),
-                buff, ipv6_reach->prefix_len, VTY_NEWLINE);
-      else
-       vty_out (vty, "  Metric      : %-8d IPv6-External : %s/%d%s",
-                ntohl (ipv6_reach->metric),
-                buff, ipv6_reach->prefix_len, VTY_NEWLINE);
-    }
+  lsp_print_mt_ipv6_reach(lsp->tlv_data.ipv6_reachs, vty,
+                          ISIS_MT_IPV4_UNICAST);
+
+  /* MT IPv6 reachability tlv */
+  for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_ipv6_reachs, lnode, mt_ipv6_reachs))
+    lsp_print_mt_ipv6_reach(mt_ipv6_reachs->list, vty, mt_ipv6_reachs->mtid);
 
   /* TE IS neighbor tlv */
   lsp_print_mt_reach(lsp->tlv_data.te_is_neighs, vty,
@@ -1014,17 +1074,13 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost)
     lsp_print_mt_reach(mt_is_neigh->list, vty, dynhost, mt_is_neigh->mtid);
 
   /* TE IPv4 tlv */
-  if (lsp->tlv_data.te_ipv4_reachs)
-    for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, lnode,
-                              te_ipv4_reach))
-    {
-      /* FIXME: There should be better way to output this stuff. */
-      vty_out (vty, "  Metric      : %-8d IPv4-Extended : %s/%d%s",
-              ntohl (te_ipv4_reach->te_metric),
-              inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start,
-                                           te_ipv4_reach->control)),
-              te_ipv4_reach->control & 0x3F, VTY_NEWLINE);
-    }
+  lsp_print_mt_ipv4_reach(lsp->tlv_data.te_ipv4_reachs, vty,
+                          ISIS_MT_IPV4_UNICAST);
+
+  /* MT IPv4 reachability tlv */
+  for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_ipv4_reachs, lnode, mt_ipv4_reachs))
+    lsp_print_mt_ipv4_reach(mt_ipv4_reachs->list, vty, mt_ipv4_reachs->mtid);
+
   vty_out (vty, "%s", VTY_NEWLINE);
 
   return;
@@ -1292,6 +1348,24 @@ lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area,
     }
 }
 
+static struct list *
+tlv_get_ipv6_reach_list(struct isis_area *area, struct tlvs *tlv_data)
+{
+  uint16_t mtid = isis_area_ipv6_topology(area);
+  if (mtid == ISIS_MT_IPV4_UNICAST)
+    {
+      if (!tlv_data->ipv6_reachs)
+        {
+          tlv_data->ipv6_reachs = list_new();
+          tlv_data->ipv6_reachs->del = free_tlv;
+        }
+      return tlv_data->ipv6_reachs;
+    }
+
+  struct tlv_mt_ipv6_reachs *reachs = tlvs_get_mt_ipv6_reachs(tlv_data, mtid);
+  return reachs->list;
+}
+
 static void
 lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area,
                          struct tlvs *tlv_data)
@@ -1301,6 +1375,7 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area,
   struct prefix_ipv6 *ipv6;
   struct isis_ext_info *info;
   struct ipv6_reachability *ip6reach;
+  struct list *reach_list = NULL;
 
   er_table = get_ext_reach(area, AF_INET6, lsp->level);
   if (!er_table)
@@ -1314,11 +1389,9 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area,
       ipv6 = (struct prefix_ipv6*)&rn->p;
       info = rn->info;
 
-      if (tlv_data->ipv6_reachs == NULL)
-        {
-          tlv_data->ipv6_reachs = list_new();
-          tlv_data->ipv6_reachs->del = free_tlv;
-        }
+      if (!reach_list)
+        reach_list = tlv_get_ipv6_reach_list(area, tlv_data);
+
       ip6reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ip6reach));
       if (info->metric > MAX_WIDE_PATH_METRIC)
         ip6reach->metric = htonl(MAX_WIDE_PATH_METRIC);
@@ -1327,7 +1400,7 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area,
       ip6reach->control_info = DISTRIBUTION_EXTERNAL;
       ip6reach->prefix_len = ipv6->prefixlen;
       memcpy(ip6reach->prefix, ipv6->prefix.s6_addr, sizeof(ip6reach->prefix));
-      listnode_add(tlv_data->ipv6_reachs, ip6reach);
+      listnode_add(reach_list, ip6reach);
     }
 }
 
@@ -1356,6 +1429,7 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area)
   struct te_ipv4_reachability *te_ipreach;
   struct isis_adjacency *nei;
   struct prefix_ipv6 *ipv6, ip6prefix;
+  struct list *ipv6_reachs = NULL;
   struct ipv6_reachability *ip6reach;
   struct tlvs tlv_data;
   struct isis_lsp *lsp0 = lsp;
@@ -1591,12 +1665,9 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area)
       if (circuit->ipv6_router && circuit->ipv6_non_link &&
          circuit->ipv6_non_link->count > 0)
        {
+         if (!ipv6_reachs)
+           ipv6_reachs = tlv_get_ipv6_reach_list(area, &tlv_data);
 
-         if (tlv_data.ipv6_reachs == NULL)
-           {
-             tlv_data.ipv6_reachs = list_new ();
-             tlv_data.ipv6_reachs->del = free_tlv;
-           }
           for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6))
            {
              ip6reach =
@@ -1619,7 +1690,7 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area)
 
              memcpy (ip6reach->prefix, ip6prefix.prefix.s6_addr,
                      sizeof (ip6reach->prefix));
-             listnode_add (tlv_data.ipv6_reachs, ip6reach);
+             listnode_add (ipv6_reachs, ip6reach);
            }
        }
 
@@ -1804,35 +1875,62 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area)
                             lsp0, area, level);
     }
 
-  /* FIXME: We pass maximum te_ipv4_reachability length to the lsp_tlv_fit()
-   * for now. lsp_tlv_fit() needs to be fixed to deal with variable length
-   * TLVs (sub TLVs!). */
   while (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs))
     {
       if (lsp->tlv_data.te_ipv4_reachs == NULL)
        lsp->tlv_data.te_ipv4_reachs = list_new ();
-      lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs,
-                  &lsp->tlv_data.te_ipv4_reachs,
-                  TE_IPV4_REACH_LEN, area->lsp_frag_threshold,
-                  tlv_add_te_ipv4_reachs);
+      _lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, &lsp->tlv_data.te_ipv4_reachs,
+                   area->lsp_frag_threshold, tlv_add_te_ipv4_reachs, NULL);
       if (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs))
        lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
                             lsp0, area, level);
     }
 
+  struct tlv_mt_ipv4_reachs *mt_ipv4_reachs;
+  for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_ipv4_reachs, node, mt_ipv4_reachs))
+    {
+      while (mt_ipv4_reachs->list && listcount(mt_ipv4_reachs->list))
+        {
+          struct tlv_mt_ipv4_reachs *frag_mt_ipv4_reachs;
+
+          frag_mt_ipv4_reachs = tlvs_get_mt_ipv4_reachs(&lsp->tlv_data, mt_ipv4_reachs->mtid);
+          _lsp_tlv_fit (lsp, &mt_ipv4_reachs->list, &frag_mt_ipv4_reachs->list,
+                        area->lsp_frag_threshold, tlv_add_te_ipv4_reachs,
+                        &mt_ipv4_reachs->mtid);
+          if (mt_ipv4_reachs->list && listcount(mt_ipv4_reachs->list))
+            lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                                 lsp0, area, level);
+        }
+    }
+
   while (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs))
     {
       if (lsp->tlv_data.ipv6_reachs == NULL)
        lsp->tlv_data.ipv6_reachs = list_new ();
-      lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs,
-                  &lsp->tlv_data.ipv6_reachs,
-                  IPV6_REACH_LEN, area->lsp_frag_threshold,
-                  tlv_add_ipv6_reachs);
+      _lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs, &lsp->tlv_data.ipv6_reachs,
+                   area->lsp_frag_threshold, tlv_add_ipv6_reachs, NULL);
       if (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs))
        lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
                             lsp0, area, level);
     }
 
+  struct tlv_mt_ipv6_reachs *mt_ipv6_reachs;
+  for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_ipv6_reachs, node, mt_ipv6_reachs))
+    {
+      while (mt_ipv6_reachs->list && listcount(mt_ipv6_reachs->list))
+        {
+          struct tlv_mt_ipv6_reachs *frag_mt_ipv6_reachs;
+
+          frag_mt_ipv6_reachs = tlvs_get_mt_ipv6_reachs(&lsp->tlv_data, mt_ipv6_reachs->mtid);
+          _lsp_tlv_fit (lsp, &mt_ipv6_reachs->list, &frag_mt_ipv6_reachs->list,
+                        area->lsp_frag_threshold, tlv_add_ipv6_reachs,
+                        &mt_ipv6_reachs->mtid);
+          if (mt_ipv6_reachs->list && listcount(mt_ipv6_reachs->list))
+            lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+                                 lsp0, area, level);
+        }
+    }
+
   while (tlv_data.is_neighs && listcount (tlv_data.is_neighs))
     {
       if (lsp->tlv_data.is_neighs == NULL)
index e66c9d7d9c4febaa2ef33647eccbe66c1bd9b26a..baf72ad22c86f7ce890830b2fabfabe6bc6b0357 100644 (file)
@@ -34,6 +34,18 @@ 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")
+DEFINE_MTYPE_STATIC(ISISD, MT_IPV4_REACHS, "ISIS MT IPv4 Reachabilities for TLV")
+DEFINE_MTYPE_STATIC(ISISD, MT_IPV6_REACHS, "ISIS MT IPv6 Reachabilities for TLV")
+
+uint16_t isis_area_ipv6_topology(struct isis_area *area)
+{
+  struct isis_area_mt_setting *area_mt_setting;
+  area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_UNICAST);
+
+  if (area_mt_setting && area_mt_setting->enabled)
+    return ISIS_MT_IPV6_UNICAST;
+  return ISIS_MT_IPV4_UNICAST;
+}
 
 /* MT naming api */
 const char *isis_mtid2str(uint16_t mtid)
@@ -492,6 +504,106 @@ tlvs_get_mt_neighbors(struct tlvs *tlvs, uint16_t mtid)
   return neighbors;
 }
 
+/* TLV MT IPv4 reach api */
+struct tlv_mt_ipv4_reachs*
+tlvs_lookup_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid)
+{
+  return lookup_mt_setting(tlvs->mt_ipv4_reachs, mtid);
+}
+
+static struct tlv_mt_ipv4_reachs*
+tlvs_new_mt_ipv4_reachs(uint16_t mtid)
+{
+  struct tlv_mt_ipv4_reachs *rv;
+
+  rv = XCALLOC(MTYPE_MT_IPV4_REACHS, sizeof(*rv));
+  rv->mtid = mtid;
+  rv->list = list_new();
+
+  return rv;
+};
+
+static void
+tlvs_free_mt_ipv4_reachs(void *arg)
+{
+  struct tlv_mt_ipv4_reachs *reachs = arg;
+
+  if (reachs && reachs->list)
+    list_delete(reachs->list);
+  XFREE(MTYPE_MT_IPV4_REACHS, reachs);
+}
+
+static void
+tlvs_add_mt_ipv4_reachs(struct tlvs *tlvs, struct tlv_mt_ipv4_reachs *reachs)
+{
+  add_mt_setting(&tlvs->mt_ipv4_reachs, reachs);
+  tlvs->mt_ipv4_reachs->del = tlvs_free_mt_ipv4_reachs;
+}
+
+struct tlv_mt_ipv4_reachs*
+tlvs_get_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid)
+{
+  struct tlv_mt_ipv4_reachs *reachs;
+
+  reachs = tlvs_lookup_mt_ipv4_reachs(tlvs, mtid);
+  if (!reachs)
+    {
+      reachs = tlvs_new_mt_ipv4_reachs(mtid);
+      tlvs_add_mt_ipv4_reachs(tlvs, reachs);
+    }
+  return reachs;
+}
+
+/* TLV MT IPv6 reach api */
+struct tlv_mt_ipv6_reachs*
+tlvs_lookup_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid)
+{
+  return lookup_mt_setting(tlvs->mt_ipv6_reachs, mtid);
+}
+
+static struct tlv_mt_ipv6_reachs*
+tlvs_new_mt_ipv6_reachs(uint16_t mtid)
+{
+  struct tlv_mt_ipv6_reachs *rv;
+
+  rv = XCALLOC(MTYPE_MT_IPV6_REACHS, sizeof(*rv));
+  rv->mtid = mtid;
+  rv->list = list_new();
+
+  return rv;
+};
+
+static void
+tlvs_free_mt_ipv6_reachs(void *arg)
+{
+  struct tlv_mt_ipv6_reachs *reachs = arg;
+
+  if (reachs && reachs->list)
+    list_delete(reachs->list);
+  XFREE(MTYPE_MT_IPV6_REACHS, reachs);
+}
+
+static void
+tlvs_add_mt_ipv6_reachs(struct tlvs *tlvs, struct tlv_mt_ipv6_reachs *reachs)
+{
+  add_mt_setting(&tlvs->mt_ipv6_reachs, reachs);
+  tlvs->mt_ipv6_reachs->del = tlvs_free_mt_ipv6_reachs;
+}
+
+struct tlv_mt_ipv6_reachs*
+tlvs_get_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid)
+{
+  struct tlv_mt_ipv6_reachs *reachs;
+
+  reachs = tlvs_lookup_mt_ipv6_reachs(tlvs, mtid);
+  if (!reachs)
+    {
+      reachs = tlvs_new_mt_ipv6_reachs(mtid);
+      tlvs_add_mt_ipv6_reachs(tlvs, reachs);
+    }
+  return reachs;
+}
+
 static void
 mt_set_add(uint16_t **mt_set, unsigned int *size,
            unsigned int *index, uint16_t mtid)
index 313e992b4a17db7c04711134c4e236fd2ea76cc0..4c991dc5c95c9894893ea5bebe42007df0c5ac82 100644 (file)
@@ -71,6 +71,16 @@ struct tlv_mt_neighbors {
   struct list *list;
 };
 
+struct tlv_mt_ipv4_reachs {
+  ISIS_MT_INFO_FIELDS
+  struct list *list;
+};
+
+struct tlv_mt_ipv6_reachs {
+  ISIS_MT_INFO_FIELDS
+  struct list *list;
+};
+
 const char *isis_mtid2str(uint16_t mtid);
 uint16_t isis_str2mtid(const char *name);
 
@@ -80,9 +90,17 @@ struct isis_circuit;
 struct tlvs;
 struct te_is_neigh;
 
+uint16_t isis_area_ipv6_topology(struct isis_area *area);
+
 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 tlv_mt_ipv4_reachs* tlvs_lookup_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid);
+struct tlv_mt_ipv4_reachs* tlvs_get_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid);
+
+struct tlv_mt_ipv6_reachs* tlvs_lookup_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid);
+struct tlv_mt_ipv6_reachs* tlvs_get_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid);
+
 struct isis_area_mt_setting* area_lookup_mt_setting(struct isis_area *area,
                                                     uint16_t mtid);
 struct isis_area_mt_setting* area_new_mt_setting(struct isis_area *area,
index 41c861bf58fee74e86a44430a115a22072f013d5..b033e35a2e105356b84796fe1e1971298fc1ae37 100644 (file)
@@ -86,10 +86,14 @@ free_tlvs (struct tlvs *tlvs)
     list_delete (tlvs->ipv4_ext_reachs);
   if (tlvs->te_ipv4_reachs)
     list_delete (tlvs->te_ipv4_reachs);
+  if (tlvs->mt_ipv4_reachs)
+    list_delete (tlvs->mt_ipv4_reachs);
   if (tlvs->ipv6_addrs)
     list_delete (tlvs->ipv6_addrs);
   if (tlvs->ipv6_reachs)
     list_delete (tlvs->ipv6_reachs);
+  if (tlvs->mt_ipv6_reachs)
+    list_delete (tlvs->mt_ipv6_reachs);
 
   memset (tlvs, 0, sizeof (struct tlvs));
 
@@ -97,33 +101,43 @@ free_tlvs (struct tlvs *tlvs)
 }
 
 static int
-parse_mt_is_neighs(struct tlvs *tlvs, bool read_mtid,
-                   unsigned int length, u_char *pnt)
+parse_mtid(uint16_t *mtid, bool read_mtid,
+           unsigned int *length, u_char **pnt)
 {
-  struct list *neigh_list;
-  uint16_t mtid;
-
-  if (read_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;
-        }
+      *mtid = ISIS_MT_IPV4_UNICAST;
+      return ISIS_OK;
+    }
 
-      memcpy(&mtid_buf, pnt, sizeof(mtid_buf));
-      pnt += sizeof(mtid_buf);
-      length -= sizeof(mtid_buf);
+  uint16_t mtid_buf;
 
-      mtid = ntohs(mtid_buf) & ISIS_MT_MASK;
-    }
-  else
+  if (*length < sizeof(mtid_buf))
     {
-      mtid = ISIS_MT_IPV4_UNICAST;
+      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;
+  return ISIS_OK;
+}
+
+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;
+  int rv;
+
+  rv = parse_mtid(&mtid, read_mtid, &length, &pnt);
+  if (rv != ISIS_OK)
+    return rv;
+
   if (mtid == ISIS_MT_IPV4_UNICAST)
     {
       if (!tlvs->te_is_neighs)
@@ -173,6 +187,192 @@ parse_mt_is_neighs(struct tlvs *tlvs, bool read_mtid,
   return ISIS_OK;
 }
 
+static int
+parse_mt_ipv4_reachs(struct tlvs *tlvs, bool read_mtid,
+                     unsigned int length, u_char *pnt)
+{
+  struct list *reach_list;
+  uint16_t mtid;
+  int rv;
+
+  rv = parse_mtid(&mtid, read_mtid, &length, &pnt);
+  if (rv != ISIS_OK)
+    return rv;
+
+  if (mtid == ISIS_MT_IPV4_UNICAST)
+    {
+      if (!tlvs->te_ipv4_reachs)
+        {
+          tlvs->te_ipv4_reachs = list_new();
+          tlvs->te_ipv4_reachs->del = free_tlv;
+        }
+      reach_list = tlvs->te_ipv4_reachs;
+    }
+  else
+    {
+      struct tlv_mt_ipv4_reachs *reachs;
+
+      reachs = tlvs_get_mt_ipv4_reachs(tlvs, mtid);
+      reachs->list->del = free_tlv;
+      reach_list = reachs->list;
+    }
+
+  while (length >= 5) /* Metric + Control */
+    {
+      struct te_ipv4_reachability *reach = XCALLOC(MTYPE_ISIS_TLV, TE_IPV4_REACH_LEN);
+
+      memcpy(reach, pnt, 5); /* Metric + Control */
+      pnt += 5;
+      length -= 5;
+
+      unsigned char prefixlen = reach->control & 0x3F;
+
+      if (prefixlen > IPV4_MAX_BITLEN)
+        {
+          zlog_warn("ISIS-TLV: invalid IPv4 extended reachability prefix length %d", prefixlen);
+          XFREE(MTYPE_ISIS_TLV, reach);
+          return ISIS_WARNING;
+        }
+
+      if (length < (unsigned int)PSIZE(prefixlen))
+        {
+          zlog_warn("ISIS-TLV: invalid IPv4 extended reachability prefix too long for tlv");
+          XFREE(MTYPE_ISIS_TLV, reach);
+          return ISIS_WARNING;
+        }
+
+      memcpy(&reach->prefix_start, pnt, PSIZE(prefixlen));
+      pnt += PSIZE(prefixlen);
+      length -= PSIZE(prefixlen);
+
+      if (reach->control & TE_IPV4_HAS_SUBTLV)
+        {
+          if (length < 1)
+            {
+              zlog_warn("ISIS-TLV: invalid IPv4 extended reachability SubTLV missing");
+              XFREE(MTYPE_ISIS_TLV, reach);
+              return ISIS_WARNING;
+            }
+
+          u_char subtlv_len = *pnt;
+          pnt++;
+          length--;
+
+          if (length < subtlv_len)
+            {
+              zlog_warn("ISIS-TLV: invalid IPv4 extended reachability SubTLVs have oversize");
+              XFREE(MTYPE_ISIS_TLV, reach);
+              return ISIS_WARNING;
+            }
+
+          /* Skip Sub-TLVs for now */
+          pnt += subtlv_len;
+          length -= subtlv_len;
+        }
+      listnode_add(reach_list, reach);
+    }
+
+  if (length)
+    {
+      zlog_warn("ISIS-TLV: TE/MT ipv4 reachability TLV has trailing data");
+      return ISIS_WARNING;
+    }
+
+  return ISIS_OK;
+}
+
+static int
+parse_mt_ipv6_reachs(struct tlvs *tlvs, bool read_mtid,
+                     unsigned int length, u_char *pnt)
+{
+  struct list *reach_list;
+  uint16_t mtid;
+  int rv;
+
+  rv = parse_mtid(&mtid, read_mtid, &length, &pnt);
+  if (rv != ISIS_OK)
+    return rv;
+
+  if (mtid == ISIS_MT_IPV4_UNICAST)
+    {
+      if (!tlvs->ipv6_reachs)
+        {
+          tlvs->ipv6_reachs = list_new();
+          tlvs->ipv6_reachs->del = free_tlv;
+        }
+      reach_list = tlvs->ipv6_reachs;
+    }
+  else
+    {
+      struct tlv_mt_ipv6_reachs *reachs;
+
+      reachs = tlvs_get_mt_ipv6_reachs(tlvs, mtid);
+      reachs->list->del = free_tlv;
+      reach_list = reachs->list;
+    }
+
+  while (length >= 6) /* Metric + Control + Prefixlen */
+    {
+      struct ipv6_reachability *reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*reach));
+
+      memcpy(reach, pnt, 6); /* Metric + Control + Prefixlen */
+      pnt += 6;
+      length -= 6;
+
+      if (reach->prefix_len > IPV6_MAX_BITLEN)
+        {
+          zlog_warn("ISIS-TLV: invalid IPv6 reachability prefix length %d", reach->prefix_len);
+          XFREE(MTYPE_ISIS_TLV, reach);
+          return ISIS_WARNING;
+        }
+
+      if (length < (unsigned int)PSIZE(reach->prefix_len))
+        {
+          zlog_warn("ISIS-TLV: invalid IPv6 reachability prefix too long for tlv");
+          XFREE(MTYPE_ISIS_TLV, reach);
+          return ISIS_WARNING;
+        }
+
+      memcpy(&reach->prefix, pnt, PSIZE(reach->prefix_len));
+      pnt += PSIZE(reach->prefix_len);
+      length -= PSIZE(reach->prefix_len);
+
+      if (reach->control_info & CTRL_INFO_SUBTLVS)
+        {
+          if (length < 1)
+            {
+              zlog_warn("ISIS-TLV: invalid IPv6 reachability SubTLV missing");
+              XFREE(MTYPE_ISIS_TLV, reach);
+              return ISIS_WARNING;
+            }
+
+          u_char subtlv_len = *pnt;
+          pnt++;
+          length--;
+
+          if (length < subtlv_len)
+            {
+              zlog_warn("ISIS-TLV: invalid IPv6 reachability SubTLVs have oversize");
+              XFREE(MTYPE_ISIS_TLV, reach);
+              return ISIS_WARNING;
+            }
+
+          /* Skip Sub-TLVs for now */
+          pnt += subtlv_len;
+          length -= subtlv_len;
+        }
+      listnode_add(reach_list, reach);
+    }
+
+  if (length)
+    {
+      zlog_warn("ISIS-TLV: (MT) IPv6 reachability 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.
@@ -189,12 +389,9 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
   struct lsp_entry *lsp_entry;
   struct in_addr *ipv4_addr;
   struct ipv4_reachability *ipv4_reach;
-  struct te_ipv4_reachability *te_ipv4_reach;
   struct in6_addr *ipv6_addr;
-  struct ipv6_reachability *ipv6_reach;
-  int prefix_octets;
   int value_len, retval = ISIS_OK;
-  u_char *start = stream, *pnt = stream, *endpnt;
+  u_char *start = stream, *pnt = stream;
 
   *found = 0;
   memset (tlvs, 0, sizeof (struct tlvs));
@@ -629,71 +826,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
          break;
 
        case TE_IPV4_REACHABILITY:
-         /* +-------+-------+-------+-------+-------+-------+-------+-------+
-          * |                        TE Metric                              | 4
-          * +-------+-------+-------+-------+-------+-------+-------+-------+
-          * |  U/D  | sTLV? |               Prefix Mask Len                 | 1
-          * +-------+-------+-------+-------+-------+-------+-------+-------+
-          * |                           Prefix                              | 0-4
-          * +---------------------------------------------------------------+
-          * |                         sub tlvs                              |
-          * +---------------------------------------------------------------+
-          * :                                                               :
-          */
          *found |= TLVFLAG_TE_IPV4_REACHABILITY;
 #ifdef EXTREME_TLV_DEBUG
          zlog_debug ("ISIS-TLV (%s): IPv4 extended Reachability length %d",
-                     areatag, length);
+                     areatag, length);
 #endif /* EXTREME_TLV_DEBUG */
-         endpnt = pnt + length;
          if (*expected & TLVFLAG_TE_IPV4_REACHABILITY)
-           {
-             while (length > value_len)
-               {
-                 te_ipv4_reach = (struct te_ipv4_reachability *) pnt;
-                 if ((te_ipv4_reach->control & 0x3F) > IPV4_MAX_BITLEN)
-                   {
-                     zlog_warn ("ISIS-TLV (%s): invalid IPv4 extended reach"
-                                "ability prefix length %d", areatag,
-                                te_ipv4_reach->control & 0x3F);
-                     retval = ISIS_WARNING;
-                     break;
-                   }
-                 if (!tlvs->te_ipv4_reachs)
-                   tlvs->te_ipv4_reachs = list_new ();
-                 listnode_add (tlvs->te_ipv4_reachs, te_ipv4_reach);
-
-                 /* Metric + Control-Byte + Prefix */
-                 unsigned int entry_len = 5 + PSIZE(te_ipv4_reach->control & 0x3F);
-                 value_len += entry_len;
-                 pnt += entry_len;
-
-                 if (te_ipv4_reach->control & TE_IPV4_HAS_SUBTLV)
-                   {
-                     if (length <= value_len)
-                       {
-                         zlog_warn("ISIS-TLV (%s): invalid IPv4 extended reachability SubTLV missing",
-                                   areatag);
-                         retval = ISIS_WARNING;
-                         break;
-                       }
-                     u_char subtlv_len = *pnt;
-                     value_len += subtlv_len + 1;
-                     pnt += subtlv_len + 1;
-                     if (length < value_len)
-                       {
-                         zlog_warn("ISIS-TLV (%s): invalid IPv4 extended reachability SubTLVs have oversize",
-                                   areatag);
-                         retval = ISIS_WARNING;
-                         break;
-                       }
-                   }
-               }
-           }
-
-         pnt = endpnt;
+           retval = parse_mt_ipv4_reachs(tlvs, false, length, pnt);
+         pnt += length;
+         break;
+       case MT_IPV4_REACHABILITY:
+         *found |= TLVFLAG_TE_IPV4_REACHABILITY;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IPv4 MT Reachability length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_TE_IPV4_REACHABILITY)
+           retval = parse_mt_ipv4_reachs(tlvs, true, length, pnt);
+         pnt += length;
          break;
-
        case IPV6_ADDR:
          /* +-------+-------+-------+-------+-------+-------+-------+-------+
           * +                 IP version 6 address                          + 16
@@ -724,67 +875,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
          break;
 
        case IPV6_REACHABILITY:
-         /* +-------+-------+-------+-------+-------+-------+-------+-------+
-          * |                 Default Metric                                | 4 
-          * +-------+-------+-------+-------+-------+-------+-------+-------+
-          * |                        Control Informantion                   |
-          * +---------------------------------------------------------------+
-          * |                        IPv6 Prefix Length                     |--+
-          * +---------------------------------------------------------------+  |
-          * |                        IPv6 Prefix                            |<-+
-          * +---------------------------------------------------------------+
-          */
          *found |= TLVFLAG_IPV6_REACHABILITY;
-         endpnt = pnt + length;
-
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IPv6 Reachability length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
          if (*expected & TLVFLAG_IPV6_REACHABILITY)
-           {
-             while (length > value_len)
-               {
-                 ipv6_reach = (struct ipv6_reachability *) pnt;
-                 if (ipv6_reach->prefix_len > IPV6_MAX_BITLEN)
-                   {
-                     zlog_warn ("ISIS-TLV (%s): invalid IPv6 extended reach"
-                                "ability prefix length %d", areatag,
-                                ipv6_reach->prefix_len);
-                     retval = ISIS_WARNING;
-                     break;
-                   }
-
-                 prefix_octets = ((ipv6_reach->prefix_len + 7) / 8);
-                 value_len += prefix_octets + 6;
-                 pnt += prefix_octets + 6;
-
-                 if (ipv6_reach->control_info & CTRL_INFO_SUBTLVS)
-                   {
-                     if (length <= value_len)
-                       {
-                         zlog_warn("ISIS-TLV (%s): invalid IPv6 extended reachability SubTLV missing",
-                                   areatag);
-                         retval = ISIS_WARNING;
-                         break;
-                       }
-                     u_char subtlv_len = *pnt;
-                     value_len += subtlv_len + 1;
-                     pnt += subtlv_len + 1;
-                     if (length < value_len)
-                       {
-                         zlog_warn("ISIS-TLV (%s): invalid IPv6 extended reachability SubTLVs have oversize",
-                                   areatag);
-                         retval = ISIS_WARNING;
-                         break;
-                       }
-                   }
-                 /* FIXME: sub-tlvs */
-                 if (!tlvs->ipv6_reachs)
-                   tlvs->ipv6_reachs = list_new ();
-                 listnode_add (tlvs->ipv6_reachs, ipv6_reach);
-               }
-           }
-
-         pnt = endpnt;
+           retval = parse_mt_ipv6_reachs(tlvs, false, length, pnt);
+         pnt += length;
+         break;
+       case MT_IPV6_REACHABILITY:
+         *found |= TLVFLAG_IPV6_REACHABILITY;
+#ifdef EXTREME_TLV_DEBUG
+         zlog_debug ("ISIS-TLV (%s): IPv6 Reachability length %d",
+                     areatag, length);
+#endif /* EXTREME_TLV_DEBUG */
+         if (*expected & TLVFLAG_IPV6_REACHABILITY)
+           retval = parse_mt_ipv6_reachs(tlvs, true, length, pnt);
+         pnt += length;
          break;
-
        case WAY3_HELLO:
          /* +---------------------------------------------------------------+
           * |                  Adjacency state                              | 1
@@ -1239,37 +1348,49 @@ tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream)
 }
 
 
-int
-tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream)
+unsigned int
+tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream, void *arg)
 {
   struct listnode *node;
   struct te_ipv4_reachability *te_reach;
   u_char value[255];
   u_char *pos = value;
-  u_char prefix_size;
-  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_ipv4_reachs, node, te_reach))
     {
-      prefix_size = ((((te_reach->control & 0x3F) - 1) >> 3) + 1);
+      unsigned char prefixlen = te_reach->control & 0x3F;
+
+      if ((size_t)(pos - value) + 5 + PSIZE(prefixlen) > max_size)
+        break;
 
-      if (pos - value + (5 + prefix_size) > 255)
-       {
-         retval =
-           add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
-         if (retval != ISIS_OK)
-           return retval;
-         pos = value;
-       }
       *(u_int32_t *) pos = te_reach->te_metric;
       pos += 4;
       *pos = te_reach->control;
       pos++;
-      memcpy (pos, &te_reach->prefix_start, prefix_size);
-      pos += prefix_size;
+      memcpy (pos, &te_reach->prefix_start, PSIZE(prefixlen));
+      pos += PSIZE(prefixlen);
+      consumed++;
     }
 
-  return add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
+  if (consumed)
+    {
+      int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV4_REACHABILITY
+                                                       : TE_IPV4_REACHABILITY,
+                        pos - value, value, stream);
+      assert(rv == ISIS_OK);
+    }
+
+  return consumed;
 }
 
 int
@@ -1297,36 +1418,49 @@ tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream)
   return add_tlv (IPV6_ADDR, pos - value, value, stream);
 }
 
-int
-tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream)
+unsigned int
+tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream, void *arg)
 {
   struct listnode *node;
   struct ipv6_reachability *ip6reach;
   u_char value[255];
   u_char *pos = value;
-  int retval, prefix_octets;
+  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 (ipv6_reachs, node, ip6reach))
     {
-      if (pos - value + IPV6_MAX_BYTELEN + 6 > 255)
-       {
-         retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
-         if (retval != ISIS_OK)
-           return retval;
-         pos = value;
-       }
-      *(uint32_t *) pos = ip6reach->metric;
+      if ((size_t)(pos - value) + 6 + PSIZE(ip6reach->prefix_len) > max_size)
+        break;
+
+      *(uint32_t *)pos = ip6reach->metric;
       pos += 4;
       *pos = ip6reach->control_info;
       pos++;
-      prefix_octets = ((ip6reach->prefix_len + 7) / 8);
       *pos = ip6reach->prefix_len;
       pos++;
-      memcpy (pos, ip6reach->prefix, prefix_octets);
-      pos += prefix_octets;
+      memcpy (pos, ip6reach->prefix, PSIZE(ip6reach->prefix_len));
+      pos += PSIZE(ip6reach->prefix_len);
+      consumed++;
     }
 
-  return add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
+  if (consumed)
+    {
+      int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV6_REACHABILITY
+                                                       : IPV6_REACHABILITY,
+                        pos - value, value, stream);
+      assert(rv == ISIS_OK);
+    }
+
+  return consumed;
 }
 
 int
index f3c04baf54e34ef3fc8789d9635c01b36ad5db6b..2135f5071fcceded2721a3c4b86ffd3b719bdf69 100644 (file)
 #define MT_IS_NEIGHBOURS          222
 #define MT_ROUTER_INFORMATION     229
 #define IPV6_ADDR                 232
+#define MT_IPV4_REACHABILITY      235
 #define IPV6_REACHABILITY         236
+#define MT_IPV6_REACHABILITY      237
 #define WAY3_HELLO                240
 #define ROUTER_INFORMATION        242
 
@@ -282,8 +284,10 @@ struct tlvs
   struct list *ipv4_int_reachs;
   struct list *ipv4_ext_reachs;
   struct list *te_ipv4_reachs;
+  struct list *mt_ipv4_reachs;
   struct list *ipv6_addrs;
   struct list *ipv6_reachs;
+  struct list *mt_ipv6_reachs;
   struct isis_passwd auth_info;
 };
 
@@ -339,9 +343,9 @@ int tlv_add_dynamic_hostname (struct hostname *hostname,
 int tlv_add_lsp_entries (struct list *lsps, struct stream *stream);
 int tlv_add_ipv4_int_reachs (struct list *ipv4_reachs, struct stream *stream);
 int tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream);
-int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream);
+unsigned int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream, void *arg);
 int tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream);
-int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream);
+unsigned int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream, void *arg);
 
 int tlv_add_padding (struct stream *stream);