isisd: add support to import routes from other protocols

Signed-off-by: Christian Franke <chris@opensourcerouting.org>
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 2b478b1..7c64eac 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -5,9 +5,10 @@
  * Copyright (C) 2001,2002   Sampo Saaristo
  *                           Tampere University of Technology      
  *                           Institute of Communications Engineering
+ * Copyright (C) 2013-2015   Christian Franke <chris@opensourcerouting.org>
  *
  * This program is free software; you can redistribute it and/or modify it 
- * under the terms of the GNU General Public Licenseas published by the Free 
+ * under the terms of the GNU General Public License as published by the Free 
  * Software Foundation; either version 2 of the License, or (at your option) 
  * any later version.
  *
@@ -35,6 +36,7 @@
 #include "if.h"
 #include "checksum.h"
 #include "md5.h"
+#include "table.h"
 
 #include "isisd/dict.h"
 #include "isisd/isis_constants.h"
@@ -1146,6 +1148,119 @@
   return lsp;
 }
 
+static void
+lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area,
+                         struct tlvs *tlv_data)
+{
+  struct route_table *er_table;
+  struct route_node *rn;
+  struct prefix_ipv4 *ipv4;
+  struct isis_ext_info *info;
+  struct ipv4_reachability *ipreach;
+  struct te_ipv4_reachability *te_ipreach;
+
+  er_table = get_ext_reach(area, AF_INET, lsp->level);
+  if (!er_table)
+    return;
+
+  for (rn = route_top(er_table); rn; rn = route_next(rn))
+    {
+      if (!rn->info)
+        continue;
+
+      ipv4 = (struct prefix_ipv4*)&rn->p;
+      info = rn->info;
+      if (area->oldmetric)
+        {
+          if (tlv_data->ipv4_ext_reachs == NULL)
+            {
+              tlv_data->ipv4_ext_reachs = list_new();
+              tlv_data->ipv4_ext_reachs->del = free_tlv;
+            }
+          ipreach = XMALLOC(MTYPE_ISIS_TLV, sizeof(*ipreach));
+
+          ipreach->prefix.s_addr = ipv4->prefix.s_addr;
+          masklen2ip(ipv4->prefixlen, &ipreach->mask);
+          ipreach->prefix.s_addr &= ipreach->mask.s_addr;
+
+          if ((info->metric & 0x3f) != info->metric)
+            ipreach->metrics.metric_default = 0x3f;
+          else
+            ipreach->metrics.metric_default = info->metric;
+          ipreach->metrics.metric_expense = METRICS_UNSUPPORTED;
+          ipreach->metrics.metric_error = METRICS_UNSUPPORTED;
+          ipreach->metrics.metric_delay = METRICS_UNSUPPORTED;
+          listnode_add(tlv_data->ipv4_ext_reachs, ipreach);
+        }
+      if (area->newmetric)
+        {
+          if (tlv_data->te_ipv4_reachs == NULL)
+            {
+              tlv_data->te_ipv4_reachs = list_new();
+              tlv_data->te_ipv4_reachs->del = free_tlv;
+            }
+          te_ipreach =
+              XCALLOC(MTYPE_ISIS_TLV,
+                      sizeof(*te_ipreach) - 1 + PSIZE(ipv4->prefixlen));
+          if (info->metric > MAX_WIDE_PATH_METRIC)
+            te_ipreach->te_metric = htonl(MAX_WIDE_PATH_METRIC);
+          else
+            te_ipreach->te_metric = htonl(info->metric);
+          te_ipreach->control = ipv4->prefixlen & 0x3f;
+          memcpy(&te_ipreach->prefix_start, &ipv4->prefix.s_addr,
+                 PSIZE(ipv4->prefixlen));
+          listnode_add(tlv_data->te_ipv4_reachs, te_ipreach);
+        }
+    }
+}
+
+static void
+lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area,
+                         struct tlvs *tlv_data)
+{
+  struct route_table *er_table;
+  struct route_node *rn;
+  struct prefix_ipv6 *ipv6;
+  struct isis_ext_info *info;
+  struct ipv6_reachability *ip6reach;
+
+  er_table = get_ext_reach(area, AF_INET6, lsp->level);
+  if (!er_table)
+    return;
+
+  for (rn = route_top(er_table); rn; rn = route_next(rn))
+    {
+      if (!rn->info)
+        continue;
+
+      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;
+        }
+      ip6reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ip6reach));
+      if (info->metric > MAX_WIDE_PATH_METRIC)
+        ip6reach->metric = htonl(MAX_WIDE_PATH_METRIC);
+      else
+        ip6reach->metric = htonl(info->metric);
+      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);
+    }
+}
+
+static void
+lsp_build_ext_reach (struct isis_lsp *lsp, struct isis_area *area,
+                     struct tlvs *tlv_data)
+{
+  lsp_build_ext_reach_ipv4(lsp, area, tlv_data);
+  lsp_build_ext_reach_ipv6(lsp, area, tlv_data);
+}
+
 /*
  * Builds the LSP data part. This func creates a new frag whenever 
  * area->lsp_frag_threshold is exceeded.
@@ -1561,6 +1676,8 @@
 	}
     }
 
+  lsp_build_ext_reach(lsp, area, &tlv_data);
+
   lsp_debug("ISIS (%s): LSP construction is complete. Serializing...", area->area_tag);
 
   while (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs))
@@ -1570,12 +1687,25 @@
       lsp_tlv_fit (lsp, &tlv_data.ipv4_int_reachs,
 		   &lsp->tlv_data.ipv4_int_reachs,
 		   IPV4_REACH_LEN, area->lsp_frag_threshold,
-		   tlv_add_ipv4_reachs);
+		   tlv_add_ipv4_int_reachs);
       if (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs))
 	lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
 			     lsp0, area, level);
     }
 
+  while (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs))
+    {
+      if (lsp->tlv_data.ipv4_ext_reachs == NULL)
+	lsp->tlv_data.ipv4_ext_reachs = list_new ();
+      lsp_tlv_fit (lsp, &tlv_data.ipv4_ext_reachs,
+		   &lsp->tlv_data.ipv4_ext_reachs,
+		   IPV4_REACH_LEN, area->lsp_frag_threshold,
+		   tlv_add_ipv4_ext_reachs);
+      if (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs))
+	lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1,
+			     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!). */