* isis_lsp.c: Support for originating TE IS and IPv4 TLV's. No any sub
	  TLV handling yet.
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index b7ddfd9..882c857 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -120,6 +120,8 @@
     }
   if (lsp->tlv_data.is_neighs)
     list_delete (lsp->tlv_data.is_neighs);
+  if (lsp->tlv_data.te_is_neighs)
+    list_delete (lsp->tlv_data.te_is_neighs);
   if (lsp->tlv_data.area_addrs)
     list_delete (lsp->tlv_data.area_addrs);
   if (lsp->tlv_data.es_neighs)
@@ -130,6 +132,8 @@
     list_delete (lsp->tlv_data.ipv4_int_reachs);
   if (lsp->tlv_data.ipv4_ext_reachs)
     list_delete (lsp->tlv_data.ipv4_ext_reachs);
+  if (lsp->tlv_data.te_ipv4_reachs)
+    list_delete (lsp->tlv_data.te_ipv4_reachs);
 #ifdef HAVE_IPV6
   if (lsp->tlv_data.ipv6_addrs)
     list_delete (lsp->tlv_data.ipv6_addrs);
@@ -922,6 +926,8 @@
 #define FRAG_NEEDED(S,T,I) \
   (STREAM_SIZE(S)-STREAM_REMAIN(S)+(I) > FRAG_THOLD(S,T))
 
+/* FIXME: It shouldn't be necessary to pass tlvsize here, TLVs can have
+ * variable length (TE TLVs, sub TLVs). */
 static void
 lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to,
 	     int tlvsize, int frag_thold,
@@ -1008,11 +1014,13 @@
 lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area)
 {
   struct is_neigh *is_neigh;
+  struct te_is_neigh *te_is_neigh;
   struct listnode *node, *nnode, *ipnode, *ipnnode;
   int level = lsp->level;
   struct isis_circuit *circuit;
   struct prefix_ipv4 *ipv4;
   struct ipv4_reachability *ipreach;
+  struct te_ipv4_reachability *te_ipreach;
   struct isis_adjacency *nei;
 #ifdef HAVE_IPV6
   struct prefix_ipv6 *ipv6, *ip6prefix;
@@ -1125,7 +1133,10 @@
   if (area->topology && level == 1)
     {
       if (tlv_data.is_neighs == NULL)
-        tlv_data.is_neighs = list_new ();
+	{
+	  tlv_data.is_neighs = list_new ();
+	  tlv_data.is_neighs->del = free_tlv;
+	}
       is_neigh = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
       memset (is_neigh, 0, sizeof (struct is_neigh));
 
@@ -1154,22 +1165,45 @@
       if (circuit->ip_router && circuit->ip_addrs &&
 	  circuit->ip_addrs->count > 0)
 	{
-	  if (tlv_data.ipv4_int_reachs == NULL)
+	  if (area->oldmetric)
 	    {
-	      tlv_data.ipv4_int_reachs = list_new ();
+	      if (tlv_data.ipv4_int_reachs == NULL)
+		{
+		  tlv_data.ipv4_int_reachs = list_new ();
+		  tlv_data.ipv4_int_reachs->del = free_tlv;
+		}
+	      for (ALL_LIST_ELEMENTS (circuit->ip_addrs, ipnode, ipnnode, ipv4))
+		{
+		  ipreach =
+		    XMALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv4_reachability));
+		  ipreach->metrics = circuit->metrics[level - 1];
+		  masklen2ip (ipv4->prefixlen, &ipreach->mask);
+		  ipreach->prefix.s_addr = ((ipreach->mask.s_addr) &
+					    (ipv4->prefix.s_addr));
+		  listnode_add (tlv_data.ipv4_int_reachs, ipreach);
+		}
 	      tlv_data.ipv4_int_reachs->del = free_tlv;
 	    }
-          for (ALL_LIST_ELEMENTS (circuit->ip_addrs, ipnode, ipnnode, ipv4))
+	  if (area->newmetric)
 	    {
-	      ipreach =
-		XCALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv4_reachability));
-	      ipreach->metrics = circuit->metrics[level - 1];
-	      masklen2ip (ipv4->prefixlen, &ipreach->mask);
-	      ipreach->prefix.s_addr = ((ipreach->mask.s_addr) &
-					(ipv4->prefix.s_addr));
-	      listnode_add (tlv_data.ipv4_int_reachs, ipreach);
+	      if (tlv_data.te_ipv4_reachs == NULL)
+		{
+		  tlv_data.te_ipv4_reachs = list_new ();
+		  tlv_data.te_ipv4_reachs->del = free_tlv;
+		}
+	      for (ALL_LIST_ELEMENTS (circuit->ip_addrs, ipnode, ipnnode, ipv4))
+		{
+		  /* FIXME All this assumes that we have no sub TLVs. */
+		  te_ipreach = XCALLOC (MTYPE_ISIS_TLV,
+					sizeof (struct te_ipv4_reachability) +
+					((ipv4->prefixlen + 7)/8) - 1);
+		  te_ipreach->te_metric = htonl (*circuit->te_metric);
+		  te_ipreach->control = (ipv4->prefixlen & 0x3F);
+		  memcpy (&te_ipreach->prefix_start, &ipv4->prefix.s_addr,
+			  (ipv4->prefixlen + 7)/8);
+		  listnode_add (tlv_data.te_ipv4_reachs, te_ipreach);
+		}
 	    }
-
 	}
 #ifdef HAVE_IPV6
       /*
@@ -1207,35 +1241,79 @@
 	case CIRCUIT_T_BROADCAST:
 	  if (level & circuit->circuit_is_type)
 	    {
-	      if (tlv_data.is_neighs == NULL)
+	      if (area->oldmetric)
 		{
-		  tlv_data.is_neighs = list_new ();
+		  if (tlv_data.is_neighs == NULL)
+		    {
+		      tlv_data.is_neighs = list_new ();
+		      tlv_data.is_neighs->del = free_tlv;
+		    }
+		  is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+		  if (level == 1)
+		    memcpy (is_neigh->neigh_id,
+			    circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
+		  else
+		    memcpy (is_neigh->neigh_id,
+			    circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
+		  is_neigh->metrics = circuit->metrics[level - 1];
+		  listnode_add (tlv_data.is_neighs, is_neigh);
 		  tlv_data.is_neighs->del = free_tlv;
 		}
-	      is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
-	      if (level == 1)
-		memcpy (is_neigh->neigh_id,
-			circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
-	      else
-		memcpy (is_neigh->neigh_id,
-			circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
-	      is_neigh->metrics = circuit->metrics[level - 1];
-	      listnode_add (tlv_data.is_neighs, is_neigh);
+	      if (area->newmetric)
+		{
+		  uint32_t metric;
+
+		  if (tlv_data.te_is_neighs == NULL)
+		    {
+		      tlv_data.te_is_neighs = list_new ();
+		      tlv_data.te_is_neighs->del = free_tlv;
+		    }
+		  te_is_neigh = XCALLOC (MTYPE_ISIS_TLV,
+					 sizeof (struct te_is_neigh));
+		  if (level == 1)
+		    memcpy (te_is_neigh->neigh_id,
+			    circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
+		  else
+		    memcpy (te_is_neigh->neigh_id,
+			    circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
+		  metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff);
+		  memcpy (te_is_neigh->te_metric, &metric, 3);
+		  listnode_add (tlv_data.te_is_neighs, te_is_neigh);
+		}
 	    }
 	  break;
 	case CIRCUIT_T_P2P:
 	  nei = circuit->u.p2p.neighbor;
 	  if (nei && (level & nei->circuit_t))
 	    {
-	      if (tlv_data.is_neighs == NULL)
+	      if (area->oldmetric)
 		{
-		  tlv_data.is_neighs = list_new ();
-		  tlv_data.is_neighs->del = free_tlv;
+		  if (tlv_data.is_neighs == NULL)
+		    {
+		      tlv_data.is_neighs = list_new ();
+		      tlv_data.is_neighs->del = free_tlv;
+		    }
+		  is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+		  memcpy (is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN);
+		  is_neigh->metrics = circuit->metrics[level - 1];
+		  listnode_add (tlv_data.is_neighs, is_neigh);
 		}
-	      is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
-	      memcpy (is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN);
-	      is_neigh->metrics = circuit->metrics[level - 1];
-	      listnode_add (tlv_data.is_neighs, is_neigh);
+	      if (area->newmetric)
+		{
+		  uint32_t metric;
+
+		  if (tlv_data.te_is_neighs == NULL)
+		    {
+		      tlv_data.te_is_neighs = list_new ();
+		      tlv_data.te_is_neighs->del = free_tlv;
+		    }
+		  te_is_neigh = XCALLOC (MTYPE_ISIS_TLV,
+					 sizeof (struct te_is_neigh));
+		  memcpy (te_is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN);
+		  metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff);
+		  memcpy (te_is_neigh->te_metric, &metric, 3);
+		  listnode_add (tlv_data.te_is_neighs, te_is_neigh);
+		}
 	    }
 	  break;
 	case CIRCUIT_T_STATIC_IN:
@@ -1264,6 +1342,20 @@
 	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!). */
+  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,
+		   9, area->lsp_frag_threshold, tlv_add_te_ipv4_reachs);
+      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);
+    }
 
 #ifdef  HAVE_IPV6
   while (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs))
@@ -1293,6 +1385,19 @@
 			     lsp0, area, level);
     }
 
+  while (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs))
+    {
+      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);
+      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);
+    }
+
+  free_tlvs (&tlv_data);
   return;
 }
 
@@ -1572,6 +1677,7 @@
 {
   struct isis_adjacency *adj;
   struct is_neigh *is_neigh;
+  struct te_is_neigh *te_is_neigh;
   struct es_neigh *es_neigh;
   struct list *adj_list;
   struct listnode *node, *nnode;
@@ -1592,15 +1698,30 @@
   /*
    * add self to IS neighbours 
    */
-  if (lsp->tlv_data.is_neighs == NULL)
+  if (circuit->area->oldmetric)
     {
-      lsp->tlv_data.is_neighs = list_new ();
-      lsp->tlv_data.is_neighs->del = free_tlv;
-    }
-  is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+      if (lsp->tlv_data.is_neighs == NULL)
+	{
+	  lsp->tlv_data.is_neighs = list_new ();
+	  lsp->tlv_data.is_neighs->del = free_tlv;
+	}
+      is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
 
-  memcpy (&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN);
-  listnode_add (lsp->tlv_data.is_neighs, is_neigh);
+      memcpy (&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN);
+      listnode_add (lsp->tlv_data.is_neighs, is_neigh);
+    }
+  if (circuit->area->newmetric)
+    {
+      if (lsp->tlv_data.te_is_neighs == NULL)
+	{
+	  lsp->tlv_data.te_is_neighs = list_new ();
+	  lsp->tlv_data.te_is_neighs->del = free_tlv;
+	}
+      te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct te_is_neigh));
+
+      memcpy (&te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN);
+      listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh);
+    }
 
   adj_list = list_new ();
   isis_adj_build_up_list (circuit->u.bc.adjdb[level - 1], adj_list);
@@ -1611,14 +1732,24 @@
 	{
 	  if ((level == 1 && adj->sys_type == ISIS_SYSTYPE_L1_IS) ||
 	      (level == 1 && adj->sys_type == ISIS_SYSTYPE_L2_IS &&
-	       adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
+	      adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
 	      (level == 2 && adj->sys_type == ISIS_SYSTYPE_L2_IS))
 	    {
 	      /* an IS neighbour -> add it */
-	      is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
+	      if (circuit->area->oldmetric)
+		{
+		  is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh));
 
-	      memcpy (&is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN);
-	      listnode_add (lsp->tlv_data.is_neighs, is_neigh);
+		  memcpy (&is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN);
+		  listnode_add (lsp->tlv_data.is_neighs, is_neigh);
+		}
+	      if (circuit->area->newmetric)
+		{
+		  te_is_neigh = XCALLOC (MTYPE_ISIS_TLV,
+					 sizeof (struct te_is_neigh));
+		  memcpy (&te_is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN);
+		  listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh);
+		}
 	    }
 	  else if (level == 1 && adj->sys_type == ISIS_SYSTYPE_ES)
 	    {
@@ -1655,6 +1786,9 @@
   if (lsp->tlv_data.is_neighs && listcount (lsp->tlv_data.is_neighs) > 0)
     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);
+
   if (lsp->tlv_data.es_neighs && listcount (lsp->tlv_data.es_neighs) > 0)
     tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu);