components/quagga/patches/70-isisd-trill.patch
author Brian Utterback <Brian.Utterback@Oracle.COM>
Tue, 03 Jan 2012 11:08:59 -0800
branchs11-sru
changeset 2239 f0bde9a421fe
parent 417 7c10b5cba79b
child 855 e14713202945
permissions -rw-r--r--
7119945 Update Quagga to version 0.99.19 6959678 Problem with network/quagga 7026150 Problem with network/quagga 7026153 Problem with network/quagga 7026154 Problem with network/quagga 7032783 Problem with network/quagga 7033149 Problem with network/quagga 7102678 Problem with network/quagga

diff --git configure.ac configure.ac
--- configure.ac
+++ configure.ac
@@ -202,6 +202,8 @@
 [  --disable-watchquagga   do not build watchquagga])
 AC_ARG_ENABLE(isisd,
 [  --enable-isisd          build isisd])
+AC_ARG_ENABLE(trill,
+[  --enable-trill          include trill support])
 AC_ARG_ENABLE(solaris,
 [  --enable-solaris          build solaris])
 AC_ARG_ENABLE(bgp-announce,
@@ -328,6 +330,30 @@
 AC_SUBST(ISIS_TOPOLOGY_DIR)
 AC_SUBST(ISIS_TOPOLOGY_LIB)
 
+if test "${enable_trill}" = "yes"; then
+  AC_CHECK_HEADER(net/trill.h)
+  AC_CHECK_LIB(dladm, dladm_valid_bridgename, libdladm=yes)
+  AC_MSG_CHECKING(TRILL IS-IS support)
+  if test $ac_cv_header_net_trill_h = no || \
+    test $ac_cv_lib_dladm_dladm_valid_bridgename = no; then
+    AC_MSG_RESULT(none)
+    AC_MSG_WARN([*** TRILL IS-IS support will not be built ***])
+    enable_trill=no
+  else
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(HAVE_TRILL,,Enable TRILL support)
+  fi
+fi
+if test "${enable_trill}" = "yes"; then
+  ISIS_TARGETS="isisd trilld"
+  ISIS_LIBS=-ldladm
+else
+  ISIS_TARGETS="isisd"
+  ISIS_LIBS=
+fi
+AC_SUBST(ISIS_TARGETS)
+AC_SUBST(ISIS_LIBS)
+
 if test "${enable_user}" = "yes" || test x"${enable_user}" = x""; then
   enable_user="quagga"
 elif test "${enable_user}" = "no"; then
@@ -785,28 +811,31 @@
 AC_DEFINE(ISIS_METHOD_PFPACKET,	1, [ constant value for isis method pfpacket ])
 AC_DEFINE(ISIS_METHOD_DLPI,	2, [ constant value for isis method dlpi ])
 AC_DEFINE(ISIS_METHOD_BPF,	3, [ constant value for isis method bpf ])
-AC_CHECK_HEADER(net/bpf.h)
-AC_CHECK_HEADER(sys/dlpi.h)
-AC_MSG_CHECKING(zebra IS-IS I/O method)
-if test x"$opsys" = x"gnu-linux"; then
-  AC_MSG_RESULT(pfpacket)
-  ISIS_METHOD_MACRO="ISIS_METHOD_PFPACKET"
-elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
-  AC_MSG_RESULT(DLPI)
-  ISIS_METHOD_MACRO="ISIS_METHOD_DLPI"
-else
-  if test $ac_cv_header_net_bpf_h = no; then
-    if test $ac_cv_header_sys_dlpi_h = no; then
-      AC_MSG_RESULT(none)
-      AC_MSG_WARN([*** IS-IS support will not be built ***])
-      ISISD=""
-    else
-      AC_MSG_RESULT(DLPI)
-    fi
+if test "${enable_isisd}" = "yes"; then
+  ISIS_METHOD_MACRO=
+  AC_CHECK_HEADER(net/bpf.h)
+  AC_CHECK_HEADER(sys/dlpi.h)
+  AC_MSG_CHECKING(zebra IS-IS I/O method)
+  if test x"$opsys" = x"gnu-linux"; then
+    AC_MSG_RESULT(pfpacket)
+    ISIS_METHOD_MACRO="ISIS_METHOD_PFPACKET"
+  elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
+    AC_MSG_RESULT(DLPI)
     ISIS_METHOD_MACRO="ISIS_METHOD_DLPI"
   else
-    AC_MSG_RESULT(BPF)
-    ISIS_METHOD_MACRO="ISIS_METHOD_BPF"
+    if test $ac_cv_header_net_bpf_h = no; then
+      if test $ac_cv_header_sys_dlpi_h = no; then
+        AC_MSG_RESULT(none)
+        AC_MSG_WARN([*** IS-IS support will not be built ***])
+        ISISD=""
+      else
+        AC_MSG_RESULT(DLPI)
+      fi
+      ISIS_METHOD_MACRO="ISIS_METHOD_DLPI"
+    else
+      AC_MSG_RESULT(BPF)
+      ISIS_METHOD_MACRO="ISIS_METHOD_BPF"
+    fi
   fi
 fi
 AC_DEFINE_UNQUOTED(ISIS_METHOD, $ISIS_METHOD_MACRO, [ selected method for isis, == one of the constants ])
diff --git isisd/Makefile.am isisd/Makefile.am
index 859facd..9adcc05 100644
--- isisd/Makefile.am
+++ isisd/Makefile.am
@@ -4,14 +4,15 @@
 	   @ISIS_TOPOLOGY_INCLUDES@
 DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
 INSTALL_SDATA=@INSTALL@ -m 600
-LIBS = @LIBS@ 
+LIBS = @LIBS@ @ISIS_LIBS@
 
 AM_CFLAGS = $(PICFLAGS)
 AM_LDFLAGS = $(PILDFLAGS)
 
 noinst_LIBRARIES = libisis.a
-sbin_PROGRAMS = isisd 
+sbin_PROGRAMS = @ISIS_TARGETS@
 SUBDIRS = topology
+EXTRA_PROGRAMS = isisd trilld
 
 libisis_a_SOURCES = \
 	isis_adjacency.c isis_lsp.c dict.c isis_circuit.c isis_pdu.c \
@@ -25,13 +26,23 @@
 	isis_lsp.h dict.h isis_circuit.h isis_misc.h isis_network.h \
 	isis_zebra.h isis_dr.h isis_flags.h isis_dynhn.h isis_common.h \
 	iso_checksum.h isis_csm.h isis_events.h isis_spf.h isis_route.h \
+	isis_trill.h \
 	include-netbsd/clnp.h include-netbsd/esis.h include-netbsd/iso.h
 
 isisd_SOURCES = \
 	isis_main.c $(libisis_a_SOURCES) \
-	isis_bpf.c isis_dlpi.c isis_pfpacket.c
+	isis_bpf.c isis_dlpi.c isis_pfpacket.c isis_trilldummy.c
 
-isisd_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
+isisd_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@ @LIBM@
 
+trilld_SOURCES = \
+	isis_main.c $(libisis_a_SOURCES) isis_trill.c isis_trillio.c \
+	isis_trillvlans.c isis_trillbpdu.c
+
+trilld_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@ @LIBM@
+
+EXTRA_DIST = isis_bpf.c isis_dlpi.c isis_pfpacket.c isis_trill.c \
+	isis_trillio.c isis_trillvlans.c isis_trillbpdu.c
+
 examplesdir = $(exampledir)
 dist_examples_DATA = isisd.conf.sample
diff --git isisd/bool.h isisd/bool.h
new file mode 100644
index 0000000..e713d65
--- /dev/null
+++ isisd/bool.h
@@ -0,0 +1,25 @@
+/*
+ * IS-IS Rout(e)ing protocol               - bool.h
+ *
+ * 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 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_ISIS_BOOL_H
+#define _ZEBRA_ISIS_BOOL_H
+
+#define FALSE 0
+#define TRUE 1
+
+#endif
diff --git isisd/dict.h isisd/dict.h
index 9395d1c..0a5382c 100644
--- isisd/dict.h
+++ isisd/dict.h
@@ -124,6 +124,11 @@ extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
 extern void dict_load_end(dict_load_t *);
 extern void dict_merge(dict_t *, dict_t *);
 
+#define ALL_DICT_NODES_RO(D,dnode,data) \
+  (dnode) = dict_first((D)); \
+  (dnode) != NULL && ((data) = dnode_get((dnode)), 1); \
+  (dnode) = dict_next((D),(dnode))
+
 #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
 #ifdef KAZLIB_SIDEEFFECT_DEBUG
 #define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
diff --git isisd/isis_adjacency.c isisd/isis_adjacency.c
index aab8d1a..4b2159b 100644
--- isisd/isis_adjacency.c
+++ isisd/isis_adjacency.c
@@ -43,6 +43,9 @@
 #include "isisd/isis_dr.h"
 #include "isisd/isis_dynhn.h"
 #include "isisd/isis_pdu.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_vlans.h"
 
 extern struct isis *isis;
 
@@ -53,6 +56,10 @@ adj_alloc (u_char * id)
 
   adj = XCALLOC (MTYPE_ISIS_ADJACENCY, sizeof (struct isis_adjacency));
   memcpy (adj->sysid, id, ISIS_SYS_ID_LEN);
+  adj->lsps = list_new();
+#ifdef HAVE_TRILL
+  adj->vlans = XCALLOC (MTYPE_ISIS_TRILL_ADJVLANS, sizeof (struct trill_adj_vlans));
+#endif
 
   return adj;
 }
@@ -127,6 +134,9 @@ isis_adj_lookup_snpa (u_char * ssnpa, struct list *adjdb)
 void
 isis_delete_adj (struct isis_adjacency *adj, struct list *adjdb)
 {
+  struct listnode *node;
+  struct isis_lsp *lsp;
+
   if (!adj)
     return;
   /* When we recieve a NULL list, we will know its p2p. */
@@ -141,7 +151,18 @@ isis_delete_adj (struct isis_adjacency *adj, struct list *adjdb)
   if (adj->ipv6_addrs)
     list_delete (adj->ipv6_addrs);
 #endif
-  
+
+  /* clear adj LSPs list (tracks LSPs recvd from the adj) */
+  if (adj->lsps)
+    {
+      for (ALL_LIST_ELEMENTS_RO (adj->lsps, node, lsp))
+        lsp->adj = NULL;
+      list_delete (adj->lsps);
+    }
+
+#ifdef HAVE_VLAN
+  XFREE (MTYPE_ISIS_TRILL_ADJVLANS, adj->vlans);
+#endif
   XFREE (MTYPE_ISIS_ADJACENCY, adj);
   return;
 }
@@ -179,6 +200,10 @@ isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state state,
       list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]);
       isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1],
 				 circuit->u.bc.lan_neighs[level - 1]);
+
+      /* On adjacency state change send new pseudo LSP if we are the DR */
+      if (circuit->u.bc.is_dr[level - 1])
+        lsp_pseudo_regenerate (circuit, level);
     }
   else if (state == ISIS_ADJ_UP)
     {				/* p2p interface */
@@ -302,6 +327,11 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail)
   struct isis_dynhn *dyn;
   int level;
   struct listnode *node;
+#ifdef HAVE_TRILL
+  int vlan_count = 0;
+  int vlan_set;
+  int vlan;
+#endif
 
   dyn = dynhn_find_by_id (adj->sysid);
   if (dyn)
@@ -388,6 +418,38 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail)
 	    }
 	}
 #endif /* HAVE_IPV6 */
+
+#ifdef HAVE_TRILL
+      vty_out (vty, "    Designated VLAN: %d", adj->vlans->designated);
+      vty_out (vty, "%s    VLAN Forwarder: ", VTY_NEWLINE);
+      EACH_VLAN_SET(adj->vlans->forwarder, vlan, vlan_set)
+        {
+	   vlan_count++;
+	   if (vlan_count % 8 == 0)
+             vty_out(vty, "%s                     ", VTY_NEWLINE);
+           vty_out (vty, "%d ", vlan); 
+        }
+      vty_out (vty, "%s    Enabled VLANs: ", VTY_NEWLINE);
+      vlan_count = 0;
+      EACH_VLAN_SET(adj->vlans->enabled, vlan, vlan_set)
+        {
+	   vlan_count++;
+	   if (vlan_count % 8 == 0)
+             vty_out(vty, "%s                     ", VTY_NEWLINE);
+           vty_out (vty, "%d ", vlan); 
+        }
+      vty_out (vty, "%s    Rx VLANs: ", VTY_NEWLINE);
+      vlan_count = 0;
+      EACH_VLAN_SET(adj->vlans->seen, vlan, vlan_set)
+        {
+	   vlan_count++;
+	   if (vlan_count % 8 == 0)
+             vty_out(vty, "%s                     ", VTY_NEWLINE);
+           vty_out (vty, "%d ", vlan); 
+        }
+      vty_out (vty, "%s", VTY_NEWLINE);
+#endif /* HAVE_TRILL */
+
       vty_out (vty, "%s", VTY_NEWLINE);
     }
   return;
diff --git isisd/isis_adjacency.h isisd/isis_adjacency.h
index 99a8bb2..b966230 100644
--- isisd/isis_adjacency.h
+++ isisd/isis_adjacency.h
@@ -96,6 +96,11 @@ struct isis_adjacency
   int flaps;			/* number of adjacency flaps  */
   struct thread *t_expire;	/* expire after hold_time  */
   struct isis_circuit *circuit;	/* back pointer */
+  struct list *lsps;		/* LSPs marked with this adjacency */
+
+#ifdef HAVE_TRILL
+  struct trill_adj_vlans *vlans;
+#endif
 };
 
 struct isis_adjacency *isis_adj_lookup (u_char * sysid, struct list *adjdb);
diff --git isisd/isis_circuit.c isisd/isis_circuit.c
index af24988..8de3622 100644
--- isisd/isis_circuit.c
+++ isisd/isis_circuit.c
@@ -58,6 +58,11 @@
 #include "isisd/isis_csm.h"
 #include "isisd/isis_events.h"
 
+#ifdef HAVE_TRILL
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
+#endif
+
 extern struct thread_master *master;
 extern struct isis *isis;
 
@@ -87,6 +92,11 @@ isis_circuit_new ()
 	  circuit->metrics[i].metric_delay = METRICS_UNSUPPORTED;
 	  circuit->te_metric[i] = DEFAULT_CIRCUIT_METRICS;
 	}
+
+#ifdef HAVE_TRILL
+      circuit->vlans = XCALLOC (MTYPE_ISIS_TRILL_VLANS,
+          sizeof(struct trill_circuit_vlans));
+#endif
     }
   else
     {
@@ -126,6 +136,13 @@ isis_circuit_configure (struct isis_circuit *circuit, struct isis_area *area)
     }
   circuit->lsp_interval = LSP_INTERVAL;
 
+#ifdef HAVE_TRILL
+  circuit->vlans->pvid = DFLT_VLAN;
+  circuit->vlans->designated = DFLT_VLAN;
+  circuit->vlans->our_designated = DFLT_VLAN;
+  SET_VLAN(circuit->vlans->enabled, DFLT_VLAN);
+#endif
+
   /*
    * Add the circuit into area
    */
@@ -216,6 +233,26 @@ isis_circuit_del (struct isis_circuit *circuit)
     list_delete (circuit->ipv6_non_link);
 #endif /* HAVE_IPV6 */
 
+#ifdef HAVE_TRILL
+  if (circuit->vlans != NULL)
+    {
+      struct trill_circuit_vlans *cvlans = circuit->vlans;
+
+      if (cvlans->appvlanfwders != NULL)
+	list_delete (cvlans->appvlanfwders);
+      if (cvlans->enabled_vlans != NULL)
+	list_delete (cvlans->enabled_vlans);
+      if (cvlans->inhibit_vlans != NULL)
+	list_delete (cvlans->inhibit_vlans);
+      if (cvlans->inhibit_thread != NULL)
+	thread_cancel (cvlans->inhibit_thread);
+      XFREE (MTYPE_ISIS_TRILL_VLANS, cvlans);
+    }
+
+  if (circuit->tc_thread != NULL)
+    thread_cancel (circuit->tc_thread);
+#endif
+
   /* and lastly the circuit itself */
   XFREE (MTYPE_ISIS_CIRCUIT, circuit);
 
@@ -374,7 +411,7 @@ isis_circuit_if_add (struct isis_circuit *circuit, struct interface *ifp)
   circuit->interface = ifp;
   ifp->info = circuit;
 
-  circuit->circuit_id = ifp->ifindex % 255;	/* FIXME: Why not ? */
+  circuit->circuit_id = (ifp->ifindex % 255) + 1; /* FIXME: Why not ? */
 
   /*  isis_circuit_update_addrs (circuit, ifp); */
 
@@ -436,7 +473,7 @@ isis_circuit_update_params (struct isis_circuit *circuit,
     {
       zlog_warn ("changing circuit_id %d->%d", circuit->circuit_id,
 		 ifp->ifindex);
-      circuit->circuit_id = ifp->ifindex % 255;
+      circuit->circuit_id = (ifp->ifindex % 255) + 1;
     }
 
   /* FIXME: Why is this needed? shouldn't we compare to the area's mtu */
@@ -500,9 +537,10 @@ isis_circuit_if_del (struct isis_circuit *circuit)
   return;
 }
 
-void
+int
 isis_circuit_up (struct isis_circuit *circuit)
 {
+  int retv;
 
   if (circuit->circ_type == CIRCUIT_T_BROADCAST)
     {
@@ -565,43 +603,57 @@ isis_circuit_up (struct isis_circuit *circuit)
 		       isis_jitter (circuit->psnp_interval[1], PSNP_JITTER));
     }
 
-  /* initialize the circuit streams */
+  /* unified init for circuits; ignore warnings below this level */
+  retv = isis_sock_init (circuit);
+  if (retv == ISIS_ERROR)
+    {
+      isis_circuit_down (circuit);
+      return retv;
+    }
+
+  /* initialize the circuit streams after opening connection */
   if (circuit->rcv_stream == NULL)
     circuit->rcv_stream = stream_new (ISO_MTU (circuit));
 
   if (circuit->snd_stream == NULL)
     circuit->snd_stream = stream_new (ISO_MTU (circuit));
 
-  /* unified init for circuits */
-  isis_sock_init (circuit);
-
-#ifdef GNU_LINUX
+#if defined(GNU_LINUX) || defined(SUNOS_5)
   THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit,
 		  circuit->fd);
 #else
   THREAD_TIMER_ON (master, circuit->t_read, isis_receive, circuit,
 		   circuit->fd);
 #endif
-  return;
+  return ISIS_OK;
 }
 
 void
 isis_circuit_down (struct isis_circuit *circuit)
 {
   /* Cancel all active threads -- FIXME: wrong place */
-  /* HT: Read thread if GNU_LINUX, TIMER thread otherwise. */
+  /* HT: Read thread if GNU_LINUX or SUNOS_5, TIMER thread otherwise. */
   THREAD_OFF (circuit->t_read);
+  THREAD_TIMER_OFF (circuit->t_send_csnp[0]);
+  THREAD_TIMER_OFF (circuit->t_send_csnp[1]);
+  THREAD_TIMER_OFF (circuit->t_send_psnp[0]);
+  THREAD_TIMER_OFF (circuit->t_send_psnp[1]);
   if (circuit->circ_type == CIRCUIT_T_BROADCAST)
     {
       THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[0]);
       THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[1]);
       THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[0]);
       THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[1]);
+      THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[0]);
+      THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[1]);
     }
   else if (circuit->circ_type == CIRCUIT_T_P2P)
     {
       THREAD_TIMER_OFF (circuit->u.p2p.t_send_p2p_hello);
     }
+#ifdef HAVE_TRILL
+  THREAD_TIMER_OFF (circuit->tc_thread);
+#endif
   /* close the socket */
   close (circuit->fd);
 
diff --git isisd/isis_circuit.h isisd/isis_circuit.h
index a7e719f..fd26a27 100644
--- isisd/isis_circuit.h
+++ isisd/isis_circuit.h
@@ -137,6 +137,14 @@ struct isis_circuit
   u_int32_t ctrl_pdus_txed;	/* controlPDUsSent */
   u_int32_t desig_changes[2];	/* lanLxDesignatedIntermediateSystemChanges */
   u_int32_t rej_adjacencies;	/* rejectedAdjacencies */
+
+#ifdef HAVE_TRILL
+  struct trill_circuit_vlans *vlans;	/* TRILL VLANs */
+  u_int8_t root_bridge[8];		/* STP Root Bridge */
+  time_t root_expire;			/* time when root expires */
+  int tc_count;
+  struct thread *tc_thread;
+#endif
 };
 
 void isis_circuit_init (void);
@@ -147,7 +155,7 @@ struct isis_circuit *circuit_scan_by_ifp (struct interface *ifp);
 void isis_circuit_del (struct isis_circuit *circuit);
 void isis_circuit_configure (struct isis_circuit *circuit,
 			     struct isis_area *area);
-void isis_circuit_up (struct isis_circuit *circuit);
+int isis_circuit_up (struct isis_circuit *circuit);
 void isis_circuit_deconfigure (struct isis_circuit *circuit,
 			       struct isis_area *area);
 
diff --git isisd/isis_common.h isisd/isis_common.h
index 2633855..29baf1e 100644
--- isisd/isis_common.h
+++ isisd/isis_common.h
@@ -21,6 +21,9 @@
  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#ifndef ISIS_COMMON_H
+#define ISIS_COMMON_H
+
 /*
  * Area Address
  */
@@ -72,3 +75,5 @@ struct flags
   int maxindex;
   struct list *free_idcs;
 };
+
+#endif
diff --git isisd/isis_constants.h isisd/isis_constants.h
index 1b75ba6..50a526c 100644
--- isisd/isis_constants.h
+++ isisd/isis_constants.h
@@ -102,6 +102,10 @@
 #define IS_LEVEL_2       2
 #define IS_LEVEL_1_AND_2 3
 
+#ifdef HAVE_TRILL
+#define TRILL_ISIS_LEVEL  IS_LEVEL_1		/* Use ISIS level 1 for TRILL */
+#endif
+
 #define SNPA_ADDRSTRLEN 18
 #define ISIS_SYS_ID_LEN  6
 #define SYSID_STRLEN    24
diff --git isisd/isis_csm.c isisd/isis_csm.c
index 80d0c90..c5bb42d 100644
--- isisd/isis_csm.c
+++ isisd/isis_csm.c
@@ -110,7 +110,11 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
 	{
 	case ISIS_ENABLE:
 	  isis_circuit_configure (circuit, (struct isis_area *) arg);
-	  isis_circuit_up (circuit);
+	  if (isis_circuit_up (circuit) != ISIS_OK)
+	    {
+	      isis_circuit_deconfigure (circuit, (struct isis_area *) arg);
+	      break;
+	    }
 	  circuit->state = C_STATE_UP;
 	  isis_event_circuit_state_change (circuit, 1);
 	  listnode_delete (isis->init_circ_list, circuit);
@@ -137,7 +141,11 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
 	  break;
 	case IF_UP_FROM_Z:
 	  isis_circuit_if_add (circuit, (struct interface *) arg);
-	  isis_circuit_up (circuit);
+	  if (isis_circuit_up (circuit) != ISIS_OK)
+	    {
+	      isis_circuit_if_del (circuit);
+	      break;
+	    }
 	  circuit->state = C_STATE_UP;
 	  isis_event_circuit_state_change (circuit, 1);
 	  break;
@@ -161,12 +169,14 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
 	  zlog_warn ("circuit already connected");
 	  break;
 	case ISIS_DISABLE:
+	  isis_circuit_down (circuit);
 	  isis_circuit_deconfigure (circuit, (struct isis_area *) arg);
 	  listnode_add (isis->init_circ_list, circuit);
 	  circuit->state = C_STATE_INIT;
 	  isis_event_circuit_state_change (circuit, 0);
 	  break;
 	case IF_DOWN_FROM_Z:
+	  isis_circuit_down (circuit);
 	  isis_circuit_if_del (circuit);
 	  circuit->state = C_STATE_CONF;
 	  isis_event_circuit_state_change (circuit, 0);
diff --git isisd/isis_dlpi.c isisd/isis_dlpi.c
index 07ab547..5837201 100644
--- isisd/isis_dlpi.c
+++ isisd/isis_dlpi.c
@@ -21,6 +21,7 @@
  */
 
 #include <zebra.h>
+#include <vty.h>
 #if ISIS_METHOD == ISIS_METHOD_DLPI
 #include <net/if.h>
 #include <netinet/if_ether.h>
@@ -302,7 +303,7 @@
 static int
 open_dlpi_dev (struct isis_circuit *circuit)
 {
-  int fd, unit, retval;
+  int fd = -1, unit, retval;
   char devpath[MAXPATHLEN];
   dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl;
   ssize_t acklen;
@@ -314,20 +315,21 @@
 	circuit->interface->name);
       return ISIS_WARNING;
     }
-  
+
+  /* Try first as Style 1 */
   /* Try the vanity node first, if permitted */
   if (getenv("DLPI_DEVONLY") == NULL)
     {
-      (void) snprintf (devpath, sizeof(devpath), "/dev/net/%s",
-                      circuit->interface->name);
-      fd = dlpiopen (devpath, &acklen);
+      (void) snprintf(devpath, sizeof(devpath), "/dev/net/%s",
+        circuit->interface->name);
+      fd = dlpiopen(devpath, &acklen);
     }
-  
+
   /* Now try as an ordinary Style 1 node */
   if (fd == -1)
     {
-      (void) snprintf (devpath, sizeof (devpath), "/dev/%s",
-                      circuit->interface->name);
+      (void) snprintf(devpath, sizeof (devpath), "/dev/%s",
+        circuit->interface->name);
       unit = -1;
       fd = dlpiopen (devpath, &acklen);
     }
@@ -403,8 +405,8 @@
     case DL_100BT:
       break;
     default:
-      zlog_warn ("%s: unexpected mac type on %s: %d", __func__,
-	circuit->interface->name, dia->dl_mac_type);
+      zlog_warn ("%s: unexpected mac type on %s: %ld", __func__,
+	circuit->interface->name, (u_long)dia->dl_mac_type);
       close (fd);
       return ISIS_WARNING;
     }
diff --git isisd/isis_dr.c isisd/isis_dr.c
index 8d306c8..a481142 100644
--- isisd/isis_dr.c
+++ isisd/isis_dr.c
@@ -207,17 +207,16 @@ isis_dr_elect (struct isis_circuit *circuit, int level)
       || (adj_dr->prio[level - 1] == own_prio
 	  && memcmp (adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0))
     {
-      if (!circuit->u.bc.is_dr[level - 1])
-	{
-	  /*
-	   * We are the DR
-	   */
+      adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
+      adj_dr->dis_record[level - 1].last_dis_change = time (NULL);
 
-	  /* rotate the history log */
-	  for (ALL_LIST_ELEMENTS_RO (list, node, adj))
-            isis_check_dr_change (adj, level);
+      /* rotate the history log */
+      for (ALL_LIST_ELEMENTS_RO (list, node, adj))
+        isis_check_dr_change (adj, level);
 
-	  /* commence */
+      if (!circuit->u.bc.is_dr[level - 1])
+	{
+	  /* We are the DR, commence DR */
 	  list_delete (list);
 	  return isis_dr_commence (circuit, level);
 	}
diff --git isisd/isis_flags.h isisd/isis_flags.h
index 13dd9e1..4346eb7 100644
--- isisd/isis_flags.h
+++ isisd/isis_flags.h
@@ -27,6 +27,7 @@
 /* The grand plan is to support 1024 circuits so we have 32*32 bit flags
  * the support will be achived using the newest drafts */
 #define ISIS_MAX_CIRCUITS 32 /* = 1024 */	/*FIXME:defined in lsp.h as well */
+#define ISIS_MAX_CIRCUITS_COUNT 32 * ISIS_MAX_CIRCUITS  /* total count of max circuits */
 
 void flags_initialize (struct flags *flags);
 struct flags *new_flags (int size);
diff --git isisd/isis_lsp.c isisd/isis_lsp.c
index 48e3147..53430a7 100644
--- isisd/isis_lsp.c
+++ isisd/isis_lsp.c
@@ -38,17 +38,22 @@
 #include "isisd/dict.h"
 #include "isisd/isis_constants.h"
 #include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
 #include "isisd/isis_circuit.h"
-#include "isisd/isisd.h"
 #include "isisd/isis_tlv.h"
 #include "isisd/isis_lsp.h"
+#ifdef HAVE_TRILL
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
+#endif
+#include "isisd/isisd.h"
 #include "isisd/isis_pdu.h"
 #include "isisd/isis_dynhn.h"
 #include "isisd/isis_misc.h"
-#include "isisd/isis_flags.h"
 #include "isisd/isis_csm.h"
 #include "isisd/isis_adjacency.h"
 #include "isisd/isis_spf.h"
+#include "isisd/bool.h"
 
 #ifdef TOPOLOGY_GENERATE
 #include "spgrid.h"
@@ -138,18 +143,34 @@ lsp_clear_data (struct isis_lsp *lsp)
   if (lsp->tlv_data.ipv6_reachs)
     list_delete (lsp->tlv_data.ipv6_reachs);
 #endif /* HAVE_IPV6 */
+  if (lsp->tlv_data.router_capabilities)
+    list_delete (lsp->tlv_data.router_capabilities);
 
   memset (&lsp->tlv_data, 0, sizeof (struct tlvs));
 
   return;
 }
 
+/*
+ * clearnick is set by callers to indicate it is
+ * also safe to clear any nickname that was learnt from
+ * the LSP. LSP purge case is safe but LSP destroyed before
+ * replaced by a new LSP from the other RBridge is not. 
+ */
 static void
-lsp_destroy (struct isis_lsp *lsp)
+lsp_destroy (struct isis_lsp *lsp, int clearnick)
 {
   if (!lsp)
     return;
 
+#ifdef HAVE_TRILL
+  if (clearnick)
+    trill_nick_destroy(lsp);
+#endif
+
+  if (lsp->adj != NULL && lsp->adj->lsps != NULL)
+    listnode_delete(lsp->adj->lsps, lsp);
+
   lsp_clear_data (lsp);
 
   if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0 && lsp->lspu.frags)
@@ -173,7 +194,7 @@ lsp_db_destroy (dict_t * lspdb)
     {
       next = dict_next (lspdb, dnode);
       lsp = dnode_get (dnode);
-      lsp_destroy (lsp);
+      lsp_destroy (lsp, TRUE);
       dict_delete_free (lspdb, dnode);
       dnode = next;
     }
@@ -187,7 +208,7 @@ lsp_db_destroy (dict_t * lspdb)
  * Remove all the frags belonging to the given lsp
  */
 static void
-lsp_remove_frags (struct list *frags, dict_t * lspdb)
+lsp_remove_frags (struct list *frags, dict_t * lspdb, int clearnick)
 {
   dnode_t *dnode;
   struct listnode *lnode, *lnnode;
@@ -196,7 +217,7 @@ lsp_remove_frags (struct list *frags, dict_t * lspdb)
   for (ALL_LIST_ELEMENTS (frags, lnode, lnnode, lsp))
     {
       dnode = dict_lookup (lspdb, lsp->lsp_header->lsp_id);
-      lsp_destroy (lsp);
+      lsp_destroy (lsp, clearnick);
       dnode_destroy (dict_delete (lspdb, dnode));
     }
 
@@ -222,7 +243,7 @@ lsp_search_and_destroy (u_char * id, dict_t * lspdb)
       if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0)
 	{
 	  if (lsp->lspu.frags)
-	    lsp_remove_frags (lsp->lspu.frags, lspdb);
+	    lsp_remove_frags (lsp->lspu.frags, lspdb, FALSE);
 	}
       else
 	{
@@ -232,7 +253,7 @@ lsp_search_and_destroy (u_char * id, dict_t * lspdb)
 	  if (lsp->lspu.zero_lsp && lsp->lspu.zero_lsp->lspu.frags)
 	    listnode_delete (lsp->lspu.zero_lsp->lspu.frags, lsp);
 	}
-      lsp_destroy (lsp);
+      lsp_destroy (lsp, FALSE);
       dnode_destroy (node);
     }
 }
@@ -314,7 +335,7 @@ lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num)
     newseq = seq_num++;
 
   lsp->lsp_header->seq_num = htonl (newseq);
-  fletcher_checksum (STREAM_DATA (lsp->pdu) + 12,
+  fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
 		   ntohs (lsp->lsp_header->pdu_len) - 12, 12);
 
   return;
@@ -367,6 +388,8 @@ lsp_update_data (struct isis_lsp *lsp, struct stream *stream,
   int retval;
 
   /* copying only the relevant part of our stream */
+  if (lsp->pdu != NULL)
+    stream_free (lsp->pdu);
   lsp->pdu = stream_dup (stream);
   
   /* setting pointers to the correct place */
@@ -414,6 +437,10 @@ lsp_update_data (struct isis_lsp *lsp, struct stream *stream,
 			   (lsp->lsp_header->lsp_bits & LSPBIT_IST));
     }
 
+#ifdef HAVE_TRILL
+  if (isis->trill_active)
+    trill_parse_router_capability_tlvs (area, lsp);
+#endif
 }
 
 void
@@ -428,7 +455,6 @@ lsp_update (struct isis_lsp *lsp, struct isis_link_state_hdr *lsp_hdr,
     dnode_destroy (dict_delete (area->lspdb[level - 1], dnode));
 
   /* free the old lsp data */
-  XFREE (MTYPE_STREAM_DATA, lsp->pdu);
   lsp_clear_data (lsp);
 
   /* rebuild the lsp data */
@@ -852,11 +878,9 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost)
   if (lsp->tlv_data.te_is_neighs)
     for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, lnode, te_is_neigh))
     {
-      uint32_t metric;
-      memcpy (&metric, te_is_neigh->te_metric, 3);
       lspid_print (te_is_neigh->neigh_id, LSPid, dynhost, 0);
       vty_out (vty, "  Metric: %-10d IS-Extended %s%s",
-	       ntohl (metric << 8), LSPid, VTY_NEWLINE);
+	       GET_TE_METRIC(te_is_neigh), LSPid, VTY_NEWLINE);
     }
 
   /* TE IPv4 tlv */
@@ -933,16 +957,32 @@ lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to,
   if (!FRAG_NEEDED (lsp->pdu, frag_thold, listcount (*from) * tlvsize + 2))
     {
       tlv_build_func (*from, lsp->pdu);
-      *to = *from;
-      *from = NULL;
+      if (listcount (*to) != 0)
+	{
+	  struct listnode *node, *nextnode;
+	  void *elem;
+
+	  for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem))
+	    {
+	      listnode_add (*to, elem);
+	      list_delete_node (*from, node);
+	    }
+	}
+      else
+	{
+	  list_free (*to);
+	  *to = *from;
+	  *from = NULL;
+	}
     }
   else if (!FRAG_NEEDED (lsp->pdu, frag_thold, tlvsize + 2))
     {
       /* fit all we can */
       count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 -
 	(STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu));
-      if (count)
-	count = count / tlvsize;
+      count = count / tlvsize;
+      if (count > (int)listcount (*from))
+	count = listcount (*from);
       for (i = 0; i < count; i++)
 	{
 	  listnode_add (*to, listgetdata (listhead (*from)));
@@ -972,7 +1012,7 @@ lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area,
       lsp_clear_data (lsp);
       if (lsp0->tlv_data.auth_info.type)
 	{
-	  memcpy (&lsp->tlv_data.auth_info, &lsp->tlv_data.auth_info,
+	  memcpy (&lsp->tlv_data.auth_info, &lsp0->tlv_data.auth_info,
 		  sizeof (struct isis_passwd));
 	  tlv_add_authinfo (lsp->tlv_data.auth_info.type,
 			    lsp->tlv_data.auth_info.len,
@@ -991,7 +1031,7 @@ lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area,
    */
   if (lsp0->tlv_data.auth_info.type)
     {
-      memcpy (&lsp->tlv_data.auth_info, &lsp->tlv_data.auth_info,
+      memcpy (&lsp->tlv_data.auth_info, &lsp0->tlv_data.auth_info,
 	      sizeof (struct isis_passwd));
       tlv_add_authinfo (lsp->tlv_data.auth_info.type,
 			lsp->tlv_data.auth_info.len,
@@ -1094,6 +1134,11 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area)
   if (lsp->tlv_data.area_addrs && listcount (lsp->tlv_data.area_addrs) > 0)
     tlv_add_area_addrs (lsp->tlv_data.area_addrs, lsp->pdu);
 
+#ifdef HAVE_TRILL
+  if (isis->trill_active && CHECK_FLAG (area->trill->status, TRILL_NICK_SET))
+      tlv_add_trill_nickname (&(area->trill->nick), lsp->pdu, area);
+#endif
+
   /* IPv4 address and TE router ID TLVs. In case of the first one we don't
    * follow "C" vendor, but "J" vendor behavior - one IPv4 address is put into
    * LSP and this address is same as router id. */
@@ -1281,13 +1326,11 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area)
 		    memcpy (te_is_neigh->neigh_id,
 			    circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
 		  if (area->oldmetric)
-		    metric =
-		      ((htonl(circuit->metrics[level - 1].metric_default) >> 8)
-			      & 0xffffff);
+		    metric = circuit->metrics[level - 1].metric_default;
 		  else
-		    metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff);
+		    metric = circuit->te_metric[level - 1];
 
-		  memcpy (te_is_neigh->te_metric, &metric, 3);
+		  SET_TE_METRIC(te_is_neigh, metric);
 		  listnode_add (tlv_data.te_is_neighs, te_is_neigh);
 		}
 	    }
@@ -1320,8 +1363,8 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area)
 		  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);
+		  metric = circuit->te_metric[level - 1];
+		  SET_TE_METRIC(te_is_neigh, metric);
 		  listnode_add (tlv_data.te_is_neighs, te_is_neigh);
 		}
 	    }
@@ -1539,6 +1582,10 @@ lsp_non_pseudo_regenerate (struct isis_area *area, int level)
   if (area->ipv6_circuits)
     isis_spf_schedule6 (area, level);
 #endif
+#ifdef HAVE_TRILL
+  if (isis->trill_active)
+    isis_spf_schedule_trill (area);
+#endif
   return ISIS_OK;
 }
 
@@ -1803,7 +1850,7 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit,
     tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu);
 
   lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
-  fletcher_checksum (STREAM_DATA (lsp->pdu) + 12,
+  fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
 		   ntohs (lsp->lsp_header->pdu_len) - 12, 12);
 
   list_delete (adj_list);
@@ -1811,7 +1858,7 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit,
   return;
 }
 
-static int
+int
 lsp_pseudo_regenerate (struct isis_circuit *circuit, int level)
 {
   dict_t *lspdb = circuit->area->lspdb[level - 1];
@@ -2022,8 +2069,8 @@ lsp_tick (struct thread *thread)
 		  if (lsp->from_topology)
 		    THREAD_TIMER_OFF (lsp->t_lsp_top_ref);
 #endif /* TOPOLOGY_GENERATE */
-		  lsp_destroy (lsp);
-		  dict_delete (area->lspdb[level], dnode);
+		  lsp_destroy (lsp, TRUE);
+		  dict_delete_free (area->lspdb[level], dnode);
 		}
 	      else if (flags_any_set (lsp->SRMflags))
 		listnode_add (lsp_list, lsp);
@@ -2071,7 +2118,7 @@ lsp_purge_dr (u_char * id, struct isis_circuit *circuit, int level)
       lsp->lsp_header->pdu_len =
 	htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
       lsp->purged = 0;
-      fletcher_checksum (STREAM_DATA (lsp->pdu) + 12,
+      fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
 		       ntohs (lsp->lsp_header->pdu_len) - 12, 12);
       ISIS_FLAGS_SET_ALL (lsp->SRMflags);
     }
@@ -2227,7 +2274,7 @@ remove_topology_lsps (struct isis_area *area)
       if (lsp->from_topology)
 	{
 	  THREAD_TIMER_OFF (lsp->t_lsp_top_ref);
-	  lsp_destroy (lsp);
+	  lsp_destroy (lsp, TRUE);
 	  dict_delete (area->lspdb[0], dnode);
 	}
       dnode = dnode_next;
@@ -2325,8 +2372,6 @@ build_topology_lsp_data (struct isis_lsp *lsp, struct isis_area *area,
 
       if (area->newmetric)
 	{
-	  uint32_t metric;
-
 	  if (tlv_data.te_is_neighs == NULL)
 	    {
 	      tlv_data.te_is_neighs = list_new ();
@@ -2337,8 +2382,7 @@ build_topology_lsp_data (struct isis_lsp *lsp, struct isis_area *area,
 		  ISIS_SYS_ID_LEN);
 	  te_is_neigh->neigh_id[ISIS_SYS_ID_LEN - 1] = (to_lsp & 0xFF);
 	  te_is_neigh->neigh_id[ISIS_SYS_ID_LEN - 2] = ((to_lsp >> 8) & 0xFF);
-	  metric = ((htonl(arc->distance) >> 8) & 0xffffff);
-	  memcpy (te_is_neigh->te_metric, &metric, 3);
+	  SET_TE_METRIC(te_is_neigh, arc->distance);
 	  listnode_add (tlv_data.te_is_neighs, te_is_neigh);
 	}
     }
diff --git isisd/isis_lsp.h isisd/isis_lsp.h
index adbde78..4112681 100644
--- isisd/isis_lsp.h
+++ isisd/isis_lsp.h
@@ -74,6 +74,7 @@ int lsp_refresh_l1 (struct thread *thread);
 int lsp_refresh_l2 (struct thread *thread);
 int lsp_regenerate_schedule (struct isis_area *area);
 
+int lsp_pseudo_regenerate (struct isis_circuit *circuit, int level);
 int lsp_l1_pseudo_generate (struct isis_circuit *circuit);
 int lsp_l2_pseudo_generate (struct isis_circuit *circuit);
 int lsp_l1_refresh_pseudo (struct thread *thread);
diff --git isisd/isis_main.c isisd/isis_main.c
index 2411518..a75281d 100644
--- isisd/isis_main.c
+++ isisd/isis_main.c
@@ -43,6 +43,14 @@
 #include "isisd/isis_circuit.h"
 #include "isisd/isisd.h"
 #include "isisd/isis_dynhn.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_route.h"
+#include "isisd/isis_zebra.h"
+#ifdef HAVE_TRILL
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
+#endif
 
 /* Default configuration file name */
 #define ISISD_DEFAULT_CONFIG "isisd.conf"
@@ -51,8 +59,12 @@
 
 /* isisd privileges */
 zebra_capabilities_t _caps_p[] = {
+#ifdef HAVE_TRILL
+  ZCAP_DL_CONFIG,
+#endif
   ZCAP_NET_RAW,
-  ZCAP_BIND
+  ZCAP_BIND,
+  ZCAP_EXEC
 };
 
 struct zebra_privs_t isisd_privs = {
@@ -66,7 +78,7 @@
   .vty_group = VTY_GROUP,
 #endif
   .caps_p = _caps_p,
-  .cap_num_p = 2,
+  .cap_num_p = sizeof (_caps_p) / sizeof (*_caps_p),
   .cap_num_i = 0
 };
 
@@ -151,7 +163,10 @@
   zlog_debug ("Reload");
   /* FIXME: Clean up func call here */
   vty_reset ();
+  (void) isisd_privs.change (ZPRIVS_RAISE);
   execve (_progpath, _argv, _envp);
+  zlog_err ("Reload failed: cannot exec %s: %s", _progpath,
+      safe_strerror (errno));
 }
 
 static void
@@ -168,6 +183,9 @@
 sighup (void)
 {
   zlog_debug ("SIGHUP received");
+#ifdef HAVE_TRILL
+  if (!trill_reload())
+#endif
   reload ();
 
   return;
@@ -227,6 +245,11 @@
   char *vty_addr = NULL;
   int dryrun = 0;
 
+#if defined(__sparc) && __GNUC__ == 3
+  /* work around alignment problems in gcc 3.x on SPARC */
+  asm("ta\t6");
+#endif
+
   /* Get the programname without the preceding path. */
   progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
 
@@ -319,6 +342,22 @@
   memory_init ();
   access_list_init();
   isis_init ();
+  isis_circuit_init ();
+  isis_spf_cmds_init ();
+#ifdef HAVE_TRILL
+  install_trill_elements ();
+#endif
+
+  /* create the global 'isis' instance */
+  isis_new (0);
+
+#ifdef HAVE_TRILL
+  trill_read_config (&config_file, argc, argv);
+  /* we use the routing socket (zebra) only if TRILL is not enabled */
+  if (!isis->trill_active)
+#endif
+  isis_zebra_init ();
+
   dyn_cache_init ();
   sort_node ();
 
@@ -340,7 +379,8 @@
     }
 
   /* Process ID file creation. */
-  pid_output (pid_file);
+  if (pid_file[0] != '\0')
+    pid_output (pid_file);
 
   /* Make isis vty socket. */
   vty_serv_sock (vty_addr, vty_port, ISIS_VTYSH_PATH);
diff --git isisd/isis_misc.c isisd/isis_misc.c
index 6b565bc..dbfb601 100644
--- isisd/isis_misc.c
+++ isisd/isis_misc.c
@@ -29,6 +29,7 @@
 #include "if.h"
 #include "command.h"
 
+#include "isisd/bool.h"
 #include "isisd/dict.h"
 #include "isisd/isis_constants.h"
 #include "isisd/isis_common.h"
@@ -38,8 +39,12 @@
 
 #include "isisd/isis_tlv.h"
 #include "isisd/isis_lsp.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
 #include "isisd/isis_constants.h"
 #include "isisd/isis_adjacency.h"
+#include "isisd/isis_dynhn.h"
 
 /* staticly assigned vars for printing purposes */
 struct in_addr new_prefix;
@@ -99,10 +104,10 @@ isonet_print (u_char * from, int len)
  * extract dot from the dotted str, and insert all the number in a buff 
  */
 int
-dotformat2buff (u_char * buff, const u_char * dotted)
+dotformat2buff (u_char * buff, const char * dotted)
 {
   int dotlen, len = 0;
-  const u_char *pos = dotted;
+  const char *pos = dotted;
   u_char number[3];
   int nextdotpos = 2;
 
@@ -157,10 +162,10 @@ dotformat2buff (u_char * buff, const u_char * dotted)
  * conversion of XXXX.XXXX.XXXX to memory
  */
 int
-sysid2buff (u_char * buff, const u_char * dotted)
+sysid2buff (u_char * buff, const char * dotted)
 {
   int len = 0;
-  const u_char *pos = dotted;
+  const char *pos = dotted;
   u_char number[3];
 
   number[2] = '\0';
@@ -254,6 +259,11 @@ speaks (struct nlpids *nlpids, int family)
 {
   int i, speaks = 0;
 
+#ifdef HAVE_TRILL
+  /* TRILL has no nlpid defined */
+  if (family == AF_TRILL && isis->trill_active)
+    return TRUE;
+#endif
   if (nlpids == (struct nlpids *) NULL)
     return speaks;
   for (i = 0; i < nlpids->count; i++)
@@ -271,7 +281,7 @@ speaks (struct nlpids *nlpids, int family)
  * Returns 0 on error, IS-IS Circuit Type on ok
  */
 int
-string2circuit_t (const u_char * str)
+string2circuit_t (const char * str)
 {
 
   if (!str)
@@ -498,7 +508,6 @@ unix_hostname (void)
 {
   static struct utsname names;
   const char *hostname;
-  extern struct host host;
 
   hostname = host.name;
   if (!hostname)
@@ -509,3 +518,26 @@ unix_hostname (void)
 
   return hostname;
 }
+
+/*
+ * Returns the dynamic hostname associated with the passed system ID.
+ * If no dynamic hostname found then returns formatted system ID.
+ */
+const char *
+print_sys_hostname (u_char *sysid)
+{
+  struct isis_dynhn *dyn;
+
+  if (!sysid)
+    return "nullsysid";
+
+  /* For our system ID return our host name */
+  if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0)
+    return unix_hostname();
+
+  dyn = dynhn_find_by_id (sysid);
+  if (dyn)
+    return (const char *)dyn->name.name;
+
+  return sysid_print (sysid);
+}
diff --git isisd/isis_misc.h isisd/isis_misc.h
index d5003a8..2db48aa 100644
--- isisd/isis_misc.h
+++ isisd/isis_misc.h
@@ -24,7 +24,7 @@
 #ifndef _ZEBRA_ISIS_MISC_H
 #define _ZEBRA_ISIS_MISC_H
 
-int string2circuit_t (const u_char *);
+int string2circuit_t (const char *);
 const char *circuit_t2string (int);
 const char *syst2string (int);
 struct in_addr newprefix2inaddr (u_char * prefix_start,
@@ -33,8 +33,8 @@ struct in_addr newprefix2inaddr (u_char * prefix_start,
  * Converting input to memory stored format
  * return value of 0 indicates wrong input
  */
-int dotformat2buff (u_char *, const u_char *);
-int sysid2buff (u_char *, const u_char *);
+int dotformat2buff (u_char *, const char *);
+int sysid2buff (u_char *, const char *);
 
 /*
  * Printing functions
@@ -46,6 +46,7 @@ const char *rawlspid_print (u_char *);
 const char *time2string (u_int32_t);
 /* typedef struct nlpids nlpids; */
 char *nlpid2string (struct nlpids *);
+const char *print_sys_hostname (u_char *sysid);
 
 /*
  * misc functions
diff --git isisd/isis_pdu.c isisd/isis_pdu.c
index 4311a90..d8573aa 100644
--- isisd/isis_pdu.c
+++ isisd/isis_pdu.c
@@ -52,6 +52,11 @@
 #include "isisd/iso_checksum.h"
 #include "isisd/isis_csm.h"
 #include "isisd/isis_events.h"
+#ifdef HAVE_TRILL
+#include <net/trill.h>
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
+#endif
 
 extern struct thread_master *master;
 extern struct isis *isis;
@@ -787,9 +792,14 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa)
 
   /*
    * check if it's own interface ip match iih ip addrs
+   * If TRILL enabled bypass this check as IS-IS is used at layer-2
    */
-  if (!(found & TLVFLAG_IPV4_ADDR)
-      || !ip_match (circuit->ip_addrs, tlvs.ipv4_addrs))
+  if (
+#ifdef HAVE_TRILL
+    !isis->trill_active &&
+#endif
+    (!(found & TLVFLAG_IPV4_ADDR)
+      || !ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)))
     {
       zlog_debug
 	("ISIS-Adj: No usable IP interface addresses in LAN IIH from %s\n",
@@ -884,7 +894,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa)
       if (adj->adj_state != ISIS_ADJ_UP)
 	{
 	  for (ALL_LIST_ELEMENTS_RO (tlvs.lan_neighs, node, snpa))
-	    if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN))
+ 	    if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN))
 	    {
 	      isis_adj_state_change (adj, ISIS_ADJ_UP,
 				     "own SNPA found in LAN Neighbours TLV");
@@ -892,6 +902,11 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa)
 	}
     }
 
+#ifdef HAVE_TRILL
+  if (found & TLVFLAG_PORT_CAPABILITY && (tlvs.port_capabilities != NULL))
+    trill_process_hello(adj, tlvs.port_capabilities);
+#endif
+
 out:
   /* DEBUG_ADJ_PACKETS */
   if (isis->debugs & DEBUG_ADJ_PACKETS)
@@ -1054,6 +1069,7 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa)
 	      ((level == 2) &&
 	       (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL1)))
 	    return ISIS_WARNING;	/* Silently discard */
+	  adj = circuit->u.p2p.neighbor;
 	}
     }
 dontcheckadj:
@@ -1251,7 +1267,12 @@ dontcheckadj:
 	}
     }
   if (lsp)
-    lsp->adj = adj;
+    {
+      /* store the adjacency in LSP and add LSP to adj's LSP list */
+      lsp->adj = adj;
+      if (adj)
+        listnode_add (adj->lsps, lsp);  
+    }
   return retval;
 }
 
@@ -1534,7 +1555,11 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit,
 	ISIS_SET_FLAG (lsp->SRMflags, circuit);
       }
       /* lets free it */
-      list_free (lsp_list);
+      list_delete (lsp_list);
+
+#ifdef HAVE_TRILL
+      trill_lspdb_acquire_event (circuit, CSNPRCV);
+#endif
     }
 
   free_tlvs (&tlvs);
@@ -1653,6 +1678,11 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa)
 
   int retval = ISIS_OK;
 
+#ifdef HAVE_TRILL
+  if (isis->trill_active && circuit->vlans->rx_tci == TRILL_TCI_BPDU)
+    return trill_process_bpdu (circuit, ssnpa);
+#endif
+
   /*
    * Let's first read data from stream to the header
    */
@@ -1767,7 +1797,7 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa)
   return retval;
 }
 
-#ifdef GNU_LINUX
+#if defined(GNU_LINUX) || defined(SUNOS_5)
 int
 isis_receive (struct thread *thread)
 {
@@ -1791,6 +1821,8 @@ isis_receive (struct thread *thread)
 
   if (retval == ISIS_OK)
     retval = isis_handle_pdu (circuit, ssnpa);
+  else
+    zlog_debug("isis_receive: error %d from circuit->rx", retval);
 
   /* 
    * prepare for next packet. 
@@ -1879,6 +1911,10 @@ fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type)
   hdr->id_len = 0;		/* ISIS_SYS_ID_LEN -  0==6 */
   hdr->version2 = 1;
   hdr->max_area_addrs = 0;	/* isis->max_area_addrs -  0==3 */
+#ifdef HAVE_TRILL
+  if (isis->trill_active)
+    hdr->max_area_addrs = isis->max_area_addrs;
+#endif
 }
 
 /*
@@ -2017,6 +2053,12 @@ send_hello (struct isis_circuit *circuit, int level)
       return ISIS_WARNING;
 #endif /* HAVE_IPV6 */
 
+#ifdef HAVE_TRILL
+  if (level == TRILL_ISIS_LEVEL)
+    if (tlv_add_trill_vlans(circuit))
+      return ISIS_WARNING;
+#endif
+
   if (circuit->u.bc.pad_hellos)
     if (tlv_add_padding (circuit->snd_stream))
       return ISIS_WARNING;
@@ -2027,7 +2069,8 @@ send_hello (struct isis_circuit *circuit, int level)
 
   retval = circuit->tx (circuit, level);
   if (retval)
-    zlog_warn ("sending of LAN Level %d Hello failed", level);
+    zlog_warn ("sending of LAN Level %d Hello failed on %s", level,
+      circuit->interface->name);
 
   /* DEBUG_ADJ_PACKETS */
   if (isis->debugs & DEBUG_ADJ_PACKETS)
@@ -2070,7 +2113,12 @@ send_lan_l1_hello (struct thread *thread)
   if (circuit->u.bc.run_dr_elect[0])
     retval = isis_dr_elect (circuit, 1);
 
+#ifdef HAVE_TRILL
+  send_trill_vlan_hellos(circuit);
+  retval = ISIS_OK;
+#else
   retval = send_lan_hello (circuit, 1);
+#endif
 
   /* set next timer thread */
   THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0],
@@ -2252,6 +2300,9 @@ send_l1_csnp (struct thread *thread)
   /* set next timer thread */
   THREAD_TIMER_ON (master, circuit->t_send_csnp[0], send_l1_csnp, circuit,
 		   isis_jitter (circuit->csnp_interval[0], CSNP_JITTER));
+#ifdef HAVE_TRILL
+  trill_lspdb_acquire_event (circuit, CSNPSND);
+#endif
 
   return retval;
 }
@@ -2274,6 +2325,9 @@ send_l2_csnp (struct thread *thread)
   /* set next timer thread */
   THREAD_TIMER_ON (master, circuit->t_send_csnp[1], send_l2_csnp, circuit,
 		   isis_jitter (circuit->csnp_interval[1], CSNP_JITTER));
+#ifdef HAVE_TRILL
+  trill_lspdb_acquire_event (circuit, CSNPSND);
+#endif
 
   return retval;
 }
@@ -2399,6 +2453,10 @@ send_psnp (int level, struct isis_circuit *circuit)
 		}
 	    }
 	  list_delete (list);
+#ifdef HAVE_TRILL
+          if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+	    trill_lspdb_acquire_event (circuit, PSNPSNDTRY);
+#endif
 	}
     }
 
diff --git isisd/isis_spf.c isisd/isis_spf.c
index 5d7e9da..6e53601 100644
--- isisd/isis_spf.c
+++ isisd/isis_spf.c
@@ -37,21 +37,24 @@
 #include "isis_constants.h"
 #include "isis_common.h"
 #include "dict.h"
+#include "isis_flags.h"
+#include "isis_circuit.h"
+#include "isis_tlv.h"
+#include "isis_lsp.h"
+#ifdef HAVE_TRILL
+#include "isis_vlans.h"
+#include "isis_trill.h"
+#endif
 #include "isisd.h"
 #include "isis_misc.h"
 #include "isis_adjacency.h"
-#include "isis_circuit.h"
-#include "isis_tlv.h"
 #include "isis_pdu.h"
-#include "isis_lsp.h"
 #include "isis_dynhn.h"
 #include "isis_spf.h"
 #include "isis_route.h"
 #include "isis_csm.h"
 
-extern struct isis *isis;
 extern struct thread_master *master;
-extern struct host host;
 
 int isis_run_spf_l1 (struct thread *thread);
 int isis_run_spf_l2 (struct thread *thread);
@@ -188,7 +191,7 @@ vid2string (struct isis_vertex *vertex, u_char * buff)
 }
 #endif /* EXTREME_DEBUG */
 
-static struct isis_spftree *
+struct isis_spftree *
 isis_spftree_new ()
 {
   struct isis_spftree *tree;
@@ -209,14 +212,14 @@ static void
 isis_vertex_del (struct isis_vertex *vertex)
 {
   list_delete (vertex->Adj_N);
+  list_delete (vertex->children);
 
   XFREE (MTYPE_ISIS_VERTEX, vertex);
 
   return;
 }
 
-#if 0 /* HT: Not used yet. */
-static void
+void
 isis_spftree_del (struct isis_spftree *spftree)
 {
   spftree->tents->del = (void (*)(void *)) isis_vertex_del;
@@ -229,7 +232,6 @@ isis_spftree_del (struct isis_spftree *spftree)
 
   return;
 }
-#endif 
 
 void
 spftree_area_init (struct isis_area *area)
@@ -258,6 +260,28 @@ spftree_area_init (struct isis_area *area)
   return;
 }
 
+void
+spftree_area_del (struct isis_area *area)
+{
+  if ((area->is_type & IS_LEVEL_1) && area->spftree[0] != NULL)
+    {
+      isis_spftree_del (area->spftree[0]);
+#ifdef HAVE_IPV6
+      isis_spftree_del (area->spftree6[0]);
+#endif
+    }
+
+  if ((area->is_type & IS_LEVEL_2) && area->spftree[1] != NULL)
+    {
+      isis_spftree_del (area->spftree[1]);
+#ifdef HAVE_IPV6
+      isis_spftree_del (area->spftree6[1]);
+#endif
+    }
+
+  return;
+}
+
 static struct isis_vertex *
 isis_vertex_new (void *id, enum vertextype vtype)
 {
@@ -297,36 +321,47 @@ isis_vertex_new (void *id, enum vertextype vtype)
     }
 
   vertex->Adj_N = list_new ();
+  vertex->children = list_new ();
 
   return vertex;
 }
 
+/* 
+ * Find the system LSP: returns the LSP in our LSP database 
+ * associated with the given system ID.
+ */
+static struct isis_lsp *
+isis_root_system_lsp (struct isis_area *area, int level, u_char *sysid)
+{
+  u_char lspid[ISIS_SYS_ID_LEN + 2];
+
+  memcpy (lspid, sysid, ISIS_SYS_ID_LEN);
+  LSP_PSEUDO_ID (lspid) = 0;
+  LSP_FRAGMENT (lspid) = 0;
+  return (lsp_search (lspid, area->lspdb[level - 1]));
+}
+
 /*
  * Add this IS to the root of SPT
  */
-static void
-isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area,
-		   int level)
+static struct isis_vertex *
+isis_spf_add_root (struct isis_spftree *spftree, struct isis_area *area,
+		   int level, u_char *sysid)
 {
   struct isis_vertex *vertex;
   struct isis_lsp *lsp;
-  u_char lspid[ISIS_SYS_ID_LEN + 2];
 #ifdef EXTREME_DEBUG
   u_char buff[BUFSIZ];
 #endif /* EXTREME_DEBUG */
-  memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN);
-  LSP_PSEUDO_ID (lspid) = 0;
-  LSP_FRAGMENT (lspid) = 0;
-
-  lsp = lsp_search (lspid, area->lspdb[level - 1]);
 
+  lsp = isis_root_system_lsp (area, level, sysid);
   if (lsp == NULL)
     zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level);
 
   if (!area->oldmetric)
-    vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_TE_IS);
+    vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_TE_IS);
   else
-    vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_IS);
+    vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_IS);
 
   vertex->lsp = lsp;
 
@@ -338,7 +373,7 @@ isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area,
 	      vertex->depth, vertex->d_N);
 #endif /* EXTREME_DEBUG */
 
-  return;
+  return vertex;
 }
 
 static struct isis_vertex *
@@ -391,7 +426,8 @@ isis_find_vertex (struct list *list, void *id, enum vertextype vtype)
 static struct isis_vertex *
 isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
 		   void *id, struct isis_adjacency *adj, u_int32_t cost,
-		   int depth, int family)
+		   int depth, int family, struct isis_vertex *parent,
+		   struct isis_lsp *lsp)
 {
   struct isis_vertex *vertex, *v;
   struct listnode *node;
@@ -402,21 +438,28 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
   vertex = isis_vertex_new (id, vtype);
   vertex->d_N = cost;
   vertex->depth = depth;
+  vertex->parent = parent;
+  if (parent && (listnode_lookup (parent->children, vertex) == NULL))
+    listnode_add (parent->children, vertex);
+  /* Store the LSP we learnt the vertex from */
+  vertex->lsp = lsp;
 
   if (adj)
     listnode_add (vertex->Adj_N, adj);
 #ifdef EXTREME_DEBUG
-  zlog_debug ("ISIS-Spf: add to TENT  %s %s depth %d dist %d",
+  zlog_debug ("ISIS-Spf: add to TENT %s %s %s depth %d dist %d",
+              print_sys_hostname (vertex->N.id),
 	      vtype2string (vertex->type), vid2string (vertex, buff),
 	      vertex->depth, vertex->d_N);
 #endif /* EXTREME_DEBUG */
-  listnode_add (spftree->tents, vertex);
+
   if (list_isempty (spftree->tents))
     {
       listnode_add (spftree->tents, vertex);
       return vertex;
     }
-  
+
+  listnode_add (spftree->tents, vertex);
   /* XXX: This cant use the standard ALL_LIST_ELEMENT macro */
   for (node = listhead (spftree->tents); node; node = listnextnode (node))
     {
@@ -454,7 +497,8 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
 static struct isis_vertex *
 isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype,
 		    void *id, struct isis_adjacency *adj, u_int32_t cost,
-		    int family)
+		    int family, struct isis_vertex *parent,
+		    struct isis_lsp *lsp)
 {
   struct isis_vertex *vertex;
 
@@ -482,13 +526,13 @@ isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype,
     }
 
 add2tent:
-  return isis_spf_add2tent (spftree, vtype, id, adj, cost, 1, family);
+  return isis_spf_add2tent (spftree, vtype, id, adj, cost, 1, family, parent, lsp);
 }
 
 static void
 process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
 	   u_int16_t dist, u_int16_t depth, struct isis_adjacency *adj,
-	   int family)
+	   int family, struct isis_vertex *parent, struct isis_lsp *lsp)
 {
   struct isis_vertex *vertex;
 #ifdef EXTREME_DEBUG
@@ -503,7 +547,8 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
   if (vertex)
     {
 #ifdef EXTREME_DEBUG
-      zlog_debug ("ISIS-Spf: process_N  %s %s dist %d already found from PATH",
+      zlog_debug ("ISIS-Spf: process_N %s %s %s dist %d already found from PATH",
+	          print_sys_hostname (vertex->N.id),
 		  vtype2string (vtype), vid2string (vertex, buff), dist);
 #endif /* EXTREME_DEBUG */
       assert (dist >= vertex->d_N);
@@ -516,7 +561,8 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
     {
       /*        1) */
 #ifdef EXTREME_DEBUG
-      zlog_debug ("ISIS-Spf: process_N  %s %s dist %d",
+      zlog_debug ("ISIS-Spf: process_N %s %s %s dist %d",
+	          print_sys_hostname (vertex->N.id),
 		  vtype2string (vtype), vid2string (vertex, buff), dist);
 #endif /* EXTREME_DEBUG */
       if (vertex->d_N == dist)
@@ -540,7 +586,13 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
 	}
     }
 
-  isis_spf_add2tent (spftree, vtype, id, adj, dist, depth, family);
+#ifdef EXTREME_DEBUG
+      zlog_debug ("ISIS-Spf: process_N add2tent %s %s dist %d",
+		  print_sys_hostname(id), 
+		  vtype2string (vtype), dist);
+#endif /* EXTREME_DEBUG */
+
+  isis_spf_add2tent (spftree, vtype, id, adj, dist, depth, family, parent, lsp);
   return;
 }
 
@@ -549,7 +601,8 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
  */
 static int
 isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
-		      uint32_t cost, uint16_t depth, int family)
+		      uint32_t cost, uint16_t depth, int family,
+		      u_char *root_sysid, struct isis_vertex *parent)
 {
   struct listnode *node, *fragnode = NULL;
   u_int16_t dist;
@@ -562,13 +615,27 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
 #ifdef HAVE_IPV6
   struct ipv6_reachability *ip6reach;
 #endif /* HAVE_IPV6 */
+  struct isis_adjacency *adj = NULL;
+  static const u_char null_sysid[ISIS_SYS_ID_LEN];
 
-
-  if (!lsp->adj)
-    return ISIS_WARNING;
-  if (lsp->tlv_data.nlpids == NULL || !speaks (lsp->tlv_data.nlpids, family))
+  if (!speaks (lsp->tlv_data.nlpids, family))
     return ISIS_OK;
 
+  /* 
+   * Note the adjacency (the neighboring system we received the LSP from) when computing 
+   * the SPF with our system as the root. Adjacencies computed are used for TRILL forwarding.
+   */
+  if (memcmp (root_sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0)
+    {
+      /* 
+       * lsp->adj can be NULL if computing SPF for other TRILL RBridges
+       * when lsp->adj is NULL during processing of lsps created by us.
+       */ 
+      if (!lsp->adj)
+        return ISIS_WARNING;
+      adj = lsp->adj;
+    }
+
 lspfragloop:
   if (lsp->lsp_header->seq_num == 0)
     {
@@ -577,6 +644,10 @@ lspfragloop:
       return ISIS_WARNING;
     }
 
+#ifdef EXTREME_DEBUG
+      zlog_debug ("ISIS-Spf: process_lsp %s", print_sys_hostname(lsp->lsp_header->lsp_id));
+#endif /* EXTREME_DEBUG */
+
   if (!ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits))
     {
       if (lsp->tlv_data.is_neighs)
@@ -585,13 +656,15 @@ lspfragloop:
 	    {
 	      /* C.2.6 a) */
 	      /* Two way connectivity */
-	      if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
+	      if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
+		continue;
+	      if (!memcmp (is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN))
 		continue;
 	      dist = cost + is_neigh->metrics.metric_default;
 	      vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
 		: VTYPE_NONPSEUDO_IS;
 	      process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist,
-			 depth + 1, lsp->adj, family);
+			 depth + 1, adj, family, parent, lsp);
 	    }
 	}
       if (lsp->tlv_data.te_is_neighs)
@@ -599,15 +672,15 @@ lspfragloop:
 	  for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node,
 				     te_is_neigh))
 	    {
-	      uint32_t metric;
-	      if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
+	      if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
+		continue;
+	      if (!memcmp (te_is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN))
 		continue;
-	      memcpy (&metric, te_is_neigh->te_metric, 3);
-	      dist = cost + ntohl (metric << 8);
+	      dist = cost + GET_TE_METRIC(te_is_neigh);
 	      vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS
 		: VTYPE_NONPSEUDO_TE_IS;
 	      process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist,
-			 depth + 1, lsp->adj, family);
+			 depth + 1, adj, family, parent, lsp);
 	    }
 	}
       if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs)
@@ -621,7 +694,7 @@ lspfragloop:
 	      prefix.u.prefix4 = ipreach->prefix;
 	      prefix.prefixlen = ip_masklen (ipreach->mask);
 	      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
-			 lsp->adj, family);
+			 adj, family, parent, lsp);
 	    }
 	}
 
@@ -636,7 +709,7 @@ lspfragloop:
 	      prefix.u.prefix4 = ipreach->prefix;
 	      prefix.prefixlen = ip_masklen (ipreach->mask);
 	      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
-			 lsp->adj, family);
+			 adj, family, parent, lsp);
 	    }
 	}
       if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs)
@@ -651,7 +724,7 @@ lspfragloop:
 						   te_ipv4_reach->control);
 	      prefix.prefixlen = (te_ipv4_reach->control & 0x3F);
 	      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
-			 lsp->adj, family);
+			 adj, family, parent, lsp);
 	    }
 	}
 #ifdef HAVE_IPV6
@@ -668,7 +741,7 @@ lspfragloop:
 	      memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix,
 		      PSIZE (ip6reach->prefix_len));
 	      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
-			 lsp->adj, family);
+			 adj, family, parent, lsp);
 	    }
 	}
 #endif /* HAVE_IPV6 */
@@ -691,12 +764,22 @@ lspfragloop:
 static int
 isis_spf_process_pseudo_lsp (struct isis_spftree *spftree,
 			     struct isis_lsp *lsp, uint16_t cost,
-			     uint16_t depth, int family)
+			     uint16_t depth, int family,
+			     u_char *root_sysid,
+			     struct isis_vertex *parent)
 {
   struct listnode *node, *fragnode = NULL;
   struct is_neigh *is_neigh;
   struct te_is_neigh *te_is_neigh;
   enum vertextype vtype;
+  struct isis_adjacency *adj = NULL;
+
+  /* 
+   * Note the adjacency (the neighboring system we received the LSP from) when computing 
+   * the SPF with our system as the root. Adjacencies computed are used for TRILL forwarding.
+   */
+  if (memcmp (root_sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0)
+    adj = lsp->adj;
 
 pseudofragloop:
 
@@ -707,13 +790,17 @@ pseudofragloop:
       return ISIS_WARNING;
     }
 
+#ifdef EXTREME_DEBUG
+      zlog_debug ("ISIS-Spf: process_pseudo_lsp %s", print_sys_hostname(lsp->lsp_header->lsp_id));
+#endif /* EXTREME_DEBUG */
+
   if (lsp->tlv_data.is_neighs)
     for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh))
       {
 	vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
 	  : VTYPE_NONPSEUDO_IS;
 	/* Two way connectivity */
-	if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
+	if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
 	  continue;
 	if (isis_find_vertex
 	    (spftree->tents, (void *) is_neigh->neigh_id, vtype) == NULL
@@ -721,8 +808,8 @@ pseudofragloop:
 			       vtype) == NULL)
 	  {
 	    /* C.2.5 i) */
-	    isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, lsp->adj,
-			     cost, depth, family);
+	    isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, adj,
+			     cost, depth, family, parent, lsp);
 	  }
       }
   if (lsp->tlv_data.te_is_neighs)
@@ -731,7 +818,7 @@ pseudofragloop:
 	vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS
 	  : VTYPE_NONPSEUDO_TE_IS;
 	/* Two way connectivity */
-	if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
+	if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
 	  continue;
 	if (isis_find_vertex
 	    (spftree->tents, (void *) te_is_neigh->neigh_id, vtype) == NULL
@@ -739,8 +826,8 @@ pseudofragloop:
 				 vtype) == NULL)
 	  {
 	    /* C.2.5 i) */
-	    isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, lsp->adj,
-			       cost, depth, family);
+	    isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, adj,
+			       cost, depth, family, parent, lsp);
 	  }
       }
 
@@ -760,23 +847,67 @@ pseudofragloop:
 
 static int
 isis_spf_preload_tent (struct isis_spftree *spftree,
-		       struct isis_area *area, int level, int family)
+		       struct isis_area *area, int level, 
+		       int family, u_char *root_sysid,
+		       struct isis_vertex *parent)
 {
   struct isis_vertex *vertex;
   struct isis_circuit *circuit;
   struct listnode *cnode, *anode, *ipnode;
   struct isis_adjacency *adj;
   struct isis_lsp *lsp;
+  struct isis_lsp *root_lsp;
   struct list *adj_list;
   struct list *adjdb;
   struct prefix_ipv4 *ipv4;
   struct prefix prefix;
   int retval = ISIS_OK;
   u_char lsp_id[ISIS_SYS_ID_LEN + 2];
+  static u_char null_lsp_id[ISIS_SYS_ID_LEN + 2];
 #ifdef HAVE_IPV6
   struct prefix_ipv6 *ipv6;
 #endif /* HAVE_IPV6 */
 
+#ifdef HAVE_TRILL
+  /* 
+   * Check if computing SPF tree for another system. If computing SPF
+   * tree for another system (for TRILL) preload TENT by determining
+   * the neighboring systems of the root system by processing the root 
+   * system LSP.
+   */
+  if (isis->trill_active &&
+      memcmp (root_sysid, isis->sysid, ISIS_SYS_ID_LEN) != 0)
+    {
+      dnode_t *dnode;
+
+      memcpy (lsp_id, root_sysid, ISIS_SYS_ID_LEN);
+      LSP_PSEUDO_ID (lsp_id) = 0;
+      LSP_FRAGMENT (lsp_id) = 0;
+
+      /* should add at least one */
+      retval = ISIS_WARNING;
+      for (ALL_DICT_NODES_RO(area->lspdb[level-1], dnode, lsp))
+        {
+          if (LSP_FRAGMENT (lsp->lsp_header->lsp_id))
+            continue;
+          if (memcmp(lsp_id, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN) != 0)
+            continue;
+
+	  if (LSP_PSEUDO_ID (lsp->lsp_header->lsp_id))
+            retval = isis_spf_process_pseudo_lsp (spftree, lsp, 
+			    DEFAULT_CIRCUIT_METRICS, 0, AF_TRILL, 
+			    root_sysid, parent);
+	  else
+            retval = isis_spf_process_lsp (spftree, lsp,
+					   DEFAULT_CIRCUIT_METRICS, 1,
+					   AF_TRILL, root_sysid, parent);
+        }
+      return retval;
+    }
+#endif
+
+  root_lsp = isis_root_system_lsp (area, level, root_sysid);
+
   for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
     {
       if (circuit->state != C_STATE_UP)
@@ -800,7 +931,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
 	      prefix.u.prefix4 = ipv4->prefix;
 	      prefix.prefixlen = ipv4->prefixlen;
 	      isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix,
-				  NULL, 0, family);
+				  NULL, 0, family, parent, root_lsp);
 	    }
 	}
 #ifdef HAVE_IPV6
@@ -812,7 +943,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
 	      prefix.prefixlen = ipv6->prefixlen;
 	      prefix.u.prefix6 = ipv6->prefix;
 	      isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL,
-				  &prefix, NULL, 0, family);
+				  &prefix, NULL, 0, family, parent, root_lsp);
 	    }
 	}
 #endif /* HAVE_IPV6 */
@@ -845,21 +976,27 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
 		{
 		case ISIS_SYSTYPE_ES:
 		  isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
-				      circuit->te_metric[level - 1], family);
+				      circuit->te_metric[level - 1],
+				      family, parent, root_lsp);
 		  break;
 		case ISIS_SYSTYPE_IS:
 		case ISIS_SYSTYPE_L1_IS:
 		case ISIS_SYSTYPE_L2_IS:
 		  vertex =
-		    isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS,
+		    isis_spf_add_local (spftree,
+					area->oldmetric ? VTYPE_NONPSEUDO_IS :
+					VTYPE_NONPSEUDO_TE_IS,
 					adj->sysid, adj,
-					circuit->te_metric[level - 1], family);
+					circuit->te_metric[level - 1],
+					family, parent, root_lsp);
 		  memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN);
 		  LSP_PSEUDO_ID (lsp_id) = 0;
 		  LSP_FRAGMENT (lsp_id) = 0;
 		  lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
 		  if (!lsp)
-		    zlog_warn ("No lsp found for IS adjacency");
+		    zlog_warn ("No LSP %s found for IS adjacency L%d on %s (ID %u)",
+			rawlspid_print (lsp_id), level,
+			circuit->interface->name, circuit->circuit_id);
 		  /*          else {
 		     isis_spf_process_lsp (spftree, lsp, vertex->d_N, 1, family);
 		     } */
@@ -878,22 +1015,34 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
 	    memcpy (lsp_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
 	  else
 	    memcpy (lsp_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
+	  /* can happen during DR reboot */
+	  if (memcmp (lsp_id, null_lsp_id, ISIS_SYS_ID_LEN + 1) == 0)
+	    {
+	      if (isis->debugs & DEBUG_SPF_EVENTS)
+		zlog_debug ("ISIS-Spf: no L%d DR on %s (ID %d)",
+		    level, circuit->interface->name, circuit->circuit_id);
+	      continue;
+	    }
 	  lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
 	  adj = isis_adj_lookup (lsp_id, adjdb);
 	  /* if no adj, we are the dis or error */
 	  if (!adj && !circuit->u.bc.is_dr[level - 1])
 	    {
-	      zlog_warn ("ISIS-Spf: No adjacency found for DR");
+	      zlog_warn ("ISIS-Spf: No adjacency found for L%d DR SPF-root:%s on %s (ID %d)",
+		  level, print_sys_hostname(root_sysid),
+		  circuit->interface->name, circuit->circuit_id);
 	    }
 	  if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0)
 	    {
-	      zlog_warn ("ISIS-Spf: No lsp found for DR");
+	      zlog_warn ("ISIS-Spf: No lsp found for L%d DR SPF-root:%s on %s (ID %d)",
+		  level, print_sys_hostname(root_sysid),
+		  circuit->interface->name, circuit->circuit_id);
 	    }
 	  else
 	    {
 	      isis_spf_process_pseudo_lsp (spftree, lsp,
-				  circuit->te_metric[level - 1], 0, family);
-
+				  circuit->te_metric[level - 1], 0,
+				  family, root_sysid, parent);
 	    }
 	}
       else if (circuit->circ_type == CIRCUIT_T_P2P)
@@ -905,19 +1054,22 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
 	    {
 	    case ISIS_SYSTYPE_ES:
 	      isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
-				  circuit->te_metric[level - 1], family);
+				  circuit->te_metric[level - 1], family,
+				  parent, root_lsp);
 	      break;
 	    case ISIS_SYSTYPE_IS:
 	    case ISIS_SYSTYPE_L1_IS:
 	    case ISIS_SYSTYPE_L2_IS:
 	      if (speaks (&adj->nlpids, family))
-		isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS, adj->sysid,
+		isis_spf_add_local (spftree,
+				    area->oldmetric ? VTYPE_NONPSEUDO_IS :
+				    VTYPE_NONPSEUDO_TE_IS, adj->sysid,
 				    adj, circuit->te_metric[level - 1],
-				    family);
+				    family, parent, root_lsp);
 	      break;
 	    case ISIS_SYSTYPE_UNKNOWN:
 	    default:
-	      zlog_warn ("isis_spf_preload_tent unknow adj type");
+	      zlog_warn ("isis_spf_preload_tent unknown adj type");
 	      break;
 	    }
 	}
@@ -946,11 +1098,17 @@ add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex,
   listnode_add (spftree->paths, vertex);
 
 #ifdef EXTREME_DEBUG
-  zlog_debug ("ISIS-Spf: added  %s %s depth %d dist %d to PATHS",
+  zlog_debug ("ISIS-Spf: added %s %s %s depth %d dist %d to PATHS",
+              print_sys_hostname (vertex->N.id),
 	      vtype2string (vertex->type), vid2string (vertex, buff),
 	      vertex->depth, vertex->d_N);
 #endif /* EXTREME_DEBUG */
+
+#ifdef HAVE_TRILL
+  if (!isis->trill_active && vertex->type > VTYPE_ES)
+#else
   if (vertex->type > VTYPE_ES)
+#endif
     {
       if (listcount (vertex->Adj_N) > 0)
 	isis_route_create ((struct prefix *) &vertex->N.prefix, vertex->d_N,
@@ -969,16 +1127,17 @@ init_spt (struct isis_spftree *spftree)
   list_delete_all_node (spftree->tents);
   list_delete_all_node (spftree->paths);
   spftree->tents->del = spftree->paths->del = NULL;
-
   return;
 }
 
 static int
-isis_run_spf (struct isis_area *area, int level, int family)
+isis_run_spf (struct isis_area *area, int level, int family,
+		u_char *sysid, struct isis_spftree *calc_spftree)
 {
   int retval = ISIS_OK;
   struct listnode *node;
   struct isis_vertex *vertex;
+  struct isis_vertex *root_vertex;
   struct isis_spftree *spftree = NULL;
   u_char lsp_id[ISIS_SYS_ID_LEN + 2];
   struct isis_lsp *lsp;
@@ -986,47 +1145,63 @@ isis_run_spf (struct isis_area *area, int level, int family)
   struct route_node *rode;
   struct isis_route_info *rinfo;
 
-  if (family == AF_INET)
+  if (calc_spftree)
+    spftree = calc_spftree;
+#ifdef HAVE_TRILL
+  else if (family == AF_TRILL)
+    spftree = area->spftree[level - 1];
+#endif
+  else if (family == AF_INET)
     spftree = area->spftree[level - 1];
 #ifdef HAVE_IPV6
   else if (family == AF_INET6)
     spftree = area->spftree6[level - 1];
 #endif
-
   assert (spftree);
+  assert (sysid);
 
-  /* Make all routes in current route table inactive. */
-  if (family == AF_INET)
-    table = area->route_table[level - 1];
+#ifdef HAVE_TRILL
+  if (family != AF_TRILL)
+#endif
+   {
+     /* Make all routes in current route table inactive. */
+     if (family == AF_INET)
+       table = area->route_table[level - 1];
 #ifdef HAVE_IPV6
-  else if (family == AF_INET6)
-    table = area->route_table6[level - 1];
+     else if (family == AF_INET6)
+       table = area->route_table6[level - 1];
 #endif
 
-  for (rode = route_top (table); rode; rode = route_next (rode))
-    {
-      if (rode->info == NULL)
-        continue;
-      rinfo = rode->info;
+    for (rode = route_top (table); rode; rode = route_next (rode))
+      {
+        if (rode->info == NULL)
+          continue;
+        rinfo = rode->info;
 
-      UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
-    }
+        UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
+      }
+   }
 
   /*
    * C.2.5 Step 0
    */
   init_spt (spftree);
   /*              a) */
-  isis_spf_add_self (spftree, area, level);
+  root_vertex = isis_spf_add_root (spftree, area, level, sysid);
   /*              b) */
-  retval = isis_spf_preload_tent (spftree, area, level, family);
+  retval = isis_spf_preload_tent (spftree, area, level, family, sysid, root_vertex);
+  if (retval != ISIS_OK)
+    {
+      zlog_warn ("ISIS-Spf: failed to load TENT SPF-root:%s", print_sys_hostname(sysid));
+      goto out;
+    }
 
   /*
    * C.2.7 Step 2
    */
   if (listcount (spftree->tents) == 0)
     {
-      zlog_warn ("ISIS-Spf: TENT is empty");
+      zlog_warn ("ISIS-Spf: TENT is empty SPF-root:%s", print_sys_hostname(sysid));
       goto out;
     }
 
@@ -1034,14 +1209,24 @@ isis_run_spf (struct isis_area *area, int level, int family)
     {
       node = listhead (spftree->tents);
       vertex = listgetdata (node);
+
+#ifdef EXTREME_DEBUG
+  zlog_debug ("ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS",
+              print_sys_hostname (vertex->N.id),
+	      vtype2string (vertex->type), vertex->depth, vertex->d_N);
+#endif /* EXTREME_DEBUG */
+
       /* Remove from tent list */
       list_delete_node (spftree->tents, node);
       if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type))
 	continue;
       add_to_paths (spftree, vertex, area, level);
-      if (vertex->type == VTYPE_PSEUDO_IS ||
-	  vertex->type == VTYPE_NONPSEUDO_IS)
-	{
+      switch (vertex->type)
+        {
+	case VTYPE_PSEUDO_IS:
+	case VTYPE_NONPSEUDO_IS:
+	case VTYPE_PSEUDO_TE_IS:
+	case VTYPE_NONPSEUDO_TE_IS:
 	  memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1);
 	  LSP_FRAGMENT (lsp_id) = 0;
 	  lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
@@ -1050,13 +1235,12 @@ isis_run_spf (struct isis_area *area, int level, int family)
 	      if (LSP_PSEUDO_ID (lsp_id))
 		{
 		  isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N,
-					       vertex->depth, family);
-
+					       vertex->depth, family, sysid, vertex);
 		}
 	      else
 		{
 		  isis_spf_process_lsp (spftree, lsp, vertex->d_N,
-					vertex->depth, family);
+					vertex->depth, family, sysid, vertex);
 		}
 	    }
 	  else
@@ -1064,11 +1248,16 @@ isis_run_spf (struct isis_area *area, int level, int family)
 	      zlog_warn ("ISIS-Spf: No LSP found for %s",
 			 rawlspid_print (lsp_id));
 	    }
+	  break;
+	default:;
 	}
     }
 
 out:
-  thread_add_event (master, isis_route_validate, area, 0);
+#ifdef HAVE_TRILL
+  if (family != AF_TRILL)
+#endif
+    thread_add_event (master, isis_route_validate, area, 0);
   spftree->lastrun = time (NULL);
   spftree->pending = 0;
 
@@ -1098,7 +1287,7 @@ isis_run_spf_l1 (struct thread *thread)
     zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
 
   if (area->ip_circuits)
-    retval = isis_run_spf (area, 1, AF_INET);
+    retval = isis_run_spf (area, 1, AF_INET, isis->sysid, NULL);
 
   THREAD_TIMER_ON (master, area->spftree[0]->t_spf, isis_run_spf_l1, area,
 		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
@@ -1128,7 +1317,7 @@ isis_run_spf_l2 (struct thread *thread)
     zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF", area->area_tag);
 
   if (area->ip_circuits)
-    retval = isis_run_spf (area, 2, AF_INET);
+    retval = isis_run_spf (area, 2, AF_INET, isis->sysid, NULL);
 
   THREAD_TIMER_ON (master, area->spftree[1]->t_spf, isis_run_spf_l2, area,
 		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
@@ -1153,9 +1342,8 @@ isis_spf_schedule (struct isis_area *area, int level)
     {
       if (level == 1)
 	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, 60);
-      else
-	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, 60);
-
+      else 
+	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, 60); 
       spftree->pending = 1;
       return retval;
     }
@@ -1176,7 +1364,9 @@ isis_spf_schedule (struct isis_area *area, int level)
   else
     {
       spftree->pending = 0;
-      retval = isis_run_spf (area, level, AF_INET);
+
+      retval = isis_run_spf (area, level, AF_INET, isis->sysid, NULL);
+
       if (level == 1)
 	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area,
 			 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
@@ -1211,7 +1401,7 @@ isis_run_spf6_l1 (struct thread *thread)
     zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
 
   if (area->ipv6_circuits)
-    retval = isis_run_spf (area, 1, AF_INET6);
+    retval = isis_run_spf (area, 1, AF_INET6, isis->sysid, NULL);
 
   THREAD_TIMER_ON (master, area->spftree6[0]->t_spf, isis_run_spf6_l1, area,
 		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
@@ -1241,7 +1431,7 @@ isis_run_spf6_l2 (struct thread *thread)
     zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF.", area->area_tag);
 
   if (area->ipv6_circuits)
-    retval = isis_run_spf (area, 2, AF_INET6);
+    retval = isis_run_spf (area, 2, AF_INET6, isis->sysid, NULL);
 
   THREAD_TIMER_ON (master, area->spftree6[1]->t_spf, isis_run_spf6_l2, area,
 		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
@@ -1289,7 +1479,7 @@ isis_spf_schedule6 (struct isis_area *area, int level)
   else
     {
       spftree->pending = 0;
-      retval = isis_run_spf (area, level, AF_INET6);
+      retval = isis_run_spf (area, level, AF_INET6, isis->sysid, NULL);
 
       if (level == 1)
 	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area,
@@ -1303,51 +1493,174 @@ isis_spf_schedule6 (struct isis_area *area, int level)
 }
 #endif
 
+#ifdef HAVE_TRILL
+static int
+trill_complete_spf(struct isis_area *area)
+{
+  int retval;
+  dnode_t *dnode;
+  nicknode_t *tnode;
+
+  retval = isis_run_spf (area, TRILL_ISIS_LEVEL, AF_TRILL, isis->sysid, NULL);
+  if (retval != ISIS_OK)
+    zlog_warn ("ISIS-Spf running spf for system returned:%d", retval);
+
+  /* 
+   * Run SPF for all other RBridges in the campus as well to
+   * compute the distribution trees with other RBridges in
+   * the campus as root. 
+   */
+  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
+    {
+      retval = isis_run_spf (area, TRILL_ISIS_LEVEL, AF_TRILL,
+	                tnode->info.sysid, tnode->rdtree);
+      if (isis->debugs & DEBUG_SPF_EVENTS)
+        zlog_debug ("ISIS-Spf running spf for:%s",
+                        print_sys_hostname (tnode->info.sysid));
+      if (retval != ISIS_OK)
+        zlog_warn ("ISIS-Spf running spf for:%s returned:%d",
+                        print_sys_hostname (tnode->info.sysid), retval);
+    }
+
+  /* 
+   * Process computed SPF trees to create TRILL 
+   * forwarding and adjacency tables.
+   */
+  trill_process_spf (area);
+  return retval;
+}
+
+static int
+isis_run_spf_trill (struct thread *thread)
+{
+  struct isis_area *area;
+  int retval;
+
+  area = THREAD_ARG (thread);
+  assert (area);
+
+  area->spftree[0]->t_spf = NULL;
+
+  if (!(area->is_type & IS_LEVEL_1))
+    {
+      if (isis->debugs & DEBUG_SPF_EVENTS)
+	zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag);
+      return ISIS_WARNING;
+    }
+
+  if (isis->debugs & DEBUG_SPF_EVENTS)
+    zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
+
+  retval = trill_complete_spf(area);
+
+  THREAD_TIMER_ON (master, area->spftree[0]->t_spf, isis_run_spf_trill, area,
+		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
+
+  return retval;
+}
+
+int
+isis_spf_schedule_trill (struct isis_area *area)
+{
+  int retval = ISIS_OK;
+  struct isis_spftree *spftree = area->spftree[TRILL_ISIS_LEVEL - 1];
+  time_t diff, now = time (NULL);
+
+  if (spftree->pending)
+    return retval;
+
+  diff = now - spftree->lastrun;
+
+  /* FIXME: let's wait a minute before doing the SPF */
+  if (now - isis->uptime < 60 || isis->uptime == 0)
+    {
+      THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_trill, area, 60);
+
+      spftree->pending = 1;
+      return retval;
+    }
+
+  THREAD_TIMER_OFF (spftree->t_spf);
+
+  if (diff < MINIMUM_SPF_INTERVAL)
+    {
+      THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_trill, area,
+		       MINIMUM_SPF_INTERVAL - diff);
+
+      spftree->pending = 1;
+    }
+  else
+    {
+      spftree->pending = 0;
+
+      retval = trill_complete_spf(area);
+
+      THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_trill, area,
+		       isis_jitter (PERIODIC_SPF_INTERVAL, 10));
+    }
+
+  return retval;
+}
+#endif /* HAVE_TRILL */
+
 static void
-isis_print_paths (struct vty *vty, struct list *paths)
+isis_print_paths (struct vty *vty, struct list *paths, u_char *root_sysid)
 {
   struct listnode *node;
+  struct listnode *cnode;
   struct isis_vertex *vertex;
-  struct isis_dynhn *dyn, *nh_dyn = NULL;
+  struct isis_vertex *cvertex;
   struct isis_adjacency *adj;
 #if 0
   u_char buff[255];
 #endif /* 0 */
 
   vty_out (vty, "System Id            Metric     Next-Hop"
-	   "             Interface   SNPA%s", VTY_NEWLINE);
+	   "             Interface     SNPA        Tree%s", VTY_NEWLINE);
 
   for (ALL_LIST_ELEMENTS_RO (paths, node, vertex))
     {
-      if (vertex->type != VTYPE_NONPSEUDO_IS)
+      if (vertex->type != VTYPE_NONPSEUDO_IS &&
+	  vertex->type != VTYPE_NONPSEUDO_TE_IS)
 	continue;
-      if (memcmp (vertex->N.id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
+      if (memcmp (vertex->N.id, root_sysid, ISIS_SYS_ID_LEN) == 0)
 	{
-	  vty_out (vty, "%s             --%s", host.name?host.name:"",
-		   VTY_NEWLINE);
+	  vty_out (vty, "%-20s %-10s", print_sys_hostname (root_sysid), "--");
+	  vty_out (vty, "%-48s", "");
 	}
       else
 	{
-	  dyn = dynhn_find_by_id ((u_char *) vertex->N.id);
-	  adj = listgetdata (listhead (vertex->Adj_N));
-	  if (adj)
+	  if (listhead (vertex->Adj_N) &&
+	       (adj = listgetdata (listhead (vertex->Adj_N))))
 	    {
-	      nh_dyn = dynhn_find_by_id (adj->sysid);
-	      vty_out (vty, "%-20s %-10u %-20s %-11s %-5s%s",
-		       (dyn != NULL) ? dyn->name.name :
-		       (const u_char *)rawlspid_print ((u_char *) vertex->N.id),
-		       vertex->d_N, (nh_dyn != NULL) ? nh_dyn->name.name :
-		       (const u_char *)rawlspid_print (adj->sysid),
+	      vty_out (vty, "%-20s %-10u %-20s %-11s %-8s",
+                       print_sys_hostname (vertex->N.id),
+		       vertex->d_N, print_sys_hostname (adj->sysid),
 		       adj->circuit->interface->name,
-		       snpa_print (adj->snpa), VTY_NEWLINE);
+		       snpa_print (adj->snpa));
 	    }
 	  else
 	    {
-	      vty_out (vty, "%s              %u %s", dyn ? dyn->name.name :
-		       (const u_char *) rawlspid_print (vertex->N.id),
-		       vertex->d_N, VTY_NEWLINE);
+	      vty_out (vty, "%-20s %-10u %-48s ", 
+		      print_sys_hostname (vertex->N.id), 
+		      vertex->d_N, "");
 	    }
 	}
+
+      if (vertex->parent)
+        vty_out (vty, " %s(%d) :-> ",
+		print_sys_hostname (vertex->parent->N.id), vertex->type);
+      else
+        vty_out (vty, " :> ");
+
+      if (listcount (vertex->children) > 0)
+        {
+          for (ALL_LIST_ELEMENTS_RO (vertex->children, cnode, cvertex))
+	    vty_out (vty, "%s(%d),", 
+	         print_sys_hostname(cvertex->N.id), cvertex->type);
+	}
+      vty_out (vty, "%s", VTY_NEWLINE);
+
 #if 0
       vty_out (vty, "%s %s %u %s", vtype2string (vertex->type),
 	       vid2string (vertex, buff), vertex->d_N, VTY_NEWLINE);
@@ -1355,6 +1668,26 @@ isis_print_paths (struct vty *vty, struct list *paths)
     }
 }
 
+#ifdef HAVE_TRILL
+static void
+trill_print_paths (struct vty *vty, struct isis_area *area)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+
+  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
+  {
+    if (tnode->rdtree && tnode->rdtree->paths->count > 0)
+    {
+      vty_out (vty, "%sRBridge distribution paths for RBridge:%s%s",
+       	       VTY_NEWLINE, print_sys_hostname (tnode->info.sysid),
+	       VTY_NEWLINE);
+      isis_print_paths (vty, tnode->rdtree->paths, tnode->info.sysid);
+    }
+  }
+}
+#endif /* HAVE_TRILL */
+
 DEFUN (show_isis_topology,
        show_isis_topology_cmd,
        "show isis topology",
@@ -1381,7 +1714,7 @@ DEFUN (show_isis_topology,
 	    {
 	      vty_out (vty, "IS-IS paths to level-%d routers that speak IP%s",
 		       level + 1, VTY_NEWLINE);
-	      isis_print_paths (vty, area->spftree[level]->paths);
+	      isis_print_paths (vty, area->spftree[level]->paths, isis->sysid);
 	    }
 #ifdef HAVE_IPV6
 	  if (area->ipv6_circuits > 0 && area->spftree6[level]
@@ -1390,10 +1723,15 @@ DEFUN (show_isis_topology,
 	      vty_out (vty,
 		       "IS-IS paths to level-%d routers that speak IPv6%s",
 		       level + 1, VTY_NEWLINE);
-	      isis_print_paths (vty, area->spftree6[level]->paths);
+	      isis_print_paths (vty, area->spftree6[level]->paths, isis->sysid);
 	    }
 #endif /* HAVE_IPV6 */
 	}
+
+#ifdef HAVE_TRILL
+      if (isis->trill_active)
+	trill_print_paths (vty, area);
+#endif
     }
 
   return CMD_SUCCESS;
@@ -1423,7 +1761,7 @@ DEFUN (show_isis_topology_l1,
 	{
 	  vty_out (vty, "IS-IS paths to level-1 routers that speak IP%s",
 		   VTY_NEWLINE);
-	  isis_print_paths (vty, area->spftree[0]->paths);
+	  isis_print_paths (vty, area->spftree[0]->paths, isis->sysid);
 	}
 #ifdef HAVE_IPV6
       if (area->ipv6_circuits > 0 && area->spftree6[0]
@@ -1431,9 +1769,13 @@ DEFUN (show_isis_topology_l1,
 	{
 	  vty_out (vty, "IS-IS paths to level-1 routers that speak IPv6%s",
 		   VTY_NEWLINE);
-	  isis_print_paths (vty, area->spftree6[0]->paths);
+	  isis_print_paths (vty, area->spftree6[0]->paths, isis->sysid);
 	}
 #endif /* HAVE_IPV6 */
+#ifdef HAVE_TRILL
+      if (isis->trill_active)
+	trill_print_paths (vty, area);
+#endif
     }
 
   return CMD_SUCCESS;
@@ -1463,7 +1805,7 @@ DEFUN (show_isis_topology_l2,
 	{
 	  vty_out (vty, "IS-IS paths to level-2 routers that speak IP%s",
 		   VTY_NEWLINE);
-	  isis_print_paths (vty, area->spftree[1]->paths);
+	  isis_print_paths (vty, area->spftree[1]->paths, isis->sysid);
 	}
 #ifdef HAVE_IPV6
       if (area->ipv6_circuits > 0 && area->spftree6[1]
@@ -1471,7 +1813,7 @@ DEFUN (show_isis_topology_l2,
 	{
 	  vty_out (vty, "IS-IS paths to level-2 routers that speak IPv6%s",
 		   VTY_NEWLINE);
-	  isis_print_paths (vty, area->spftree6[1]->paths);
+	  isis_print_paths (vty, area->spftree6[1]->paths, isis->sysid);
 	}
 #endif /* HAVE_IPV6 */
     }
diff --git isisd/isis_spf.h isisd/isis_spf.h
index 6bdab2d..ece9896 100644
--- isisd/isis_spf.h
+++ isisd/isis_spf.h
@@ -54,11 +54,12 @@ struct isis_vertex
     struct prefix prefix;
   } N;
 
-  struct isis_lsp *lsp;
+  struct isis_lsp *lsp;         /* referring LSP (the LSP this vertex was learnt from) */
   u_int32_t d_N;		/* d(N) Distance from this IS      */
   u_int16_t depth;		/* The depth in the imaginary tree */
-
-  struct list *Adj_N;		/* {Adj(N)}  */
+  struct list *Adj_N;		/* {Adj(N)} next hop or neighbor list */
+  struct isis_vertex *parent;   /* parent and child links used to find adjacencies on tree */
+  struct list *children;
 };
 
 struct isis_spftree
@@ -72,10 +73,16 @@ struct isis_spftree
   u_int32_t timerun;		/* statistics */
 };
 
+struct isis_spftree * isis_spftree_new (void);
+void isis_spftree_del (struct isis_spftree *spftree);
 void spftree_area_init (struct isis_area *area);
+void spftree_area_del (struct isis_area *area);
 int isis_spf_schedule (struct isis_area *area, int level);
 void isis_spf_cmds_init (void);
 #ifdef HAVE_IPV6
 int isis_spf_schedule6 (struct isis_area *area, int level);
 #endif
+#ifdef HAVE_TRILL
+int isis_spf_schedule_trill (struct isis_area *area);
+#endif
 #endif /* _ZEBRA_ISIS_SPF_H */
diff --git isisd/isis_tlv.c isisd/isis_tlv.c
index 94fa65e..0690243 100644
--- isisd/isis_tlv.c
+++ isisd/isis_tlv.c
@@ -43,13 +43,6 @@
 #include "isisd/isis_pdu.h"
 #include "isisd/isis_lsp.h"
 
-extern struct isis *isis;
-
-/*
- * Prototypes.
- */
-int add_tlv (u_char, u_char, u_char *, struct stream *);
-
 void
 free_tlv (void *val)
 {
@@ -93,7 +86,10 @@ free_tlvs (struct tlvs *tlvs)
   if (tlvs->ipv6_reachs)
     list_delete (tlvs->ipv6_reachs);
 #endif /* HAVE_IPV6 */
-  
+  if (tlvs->router_capabilities)
+    list_delete (tlvs->router_capabilities);
+  if (tlvs->port_capabilities)
+    list_delete (tlvs->port_capabilities);
   return;
 }
 
@@ -714,6 +710,28 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
 	  pnt += length;
 	  break;
 
+        case ROUTER_CAPABILITY:
+	  /* +------+------+------+------+------+-------+
+	   * |Length|          Router ID        | Flags |
+	   * +------+------+------+------+------+-------+
+	   * |    optional sub-TLVs (0-250 octets)      |
+	   * +------+------+------+------+------+-------+
+	   */
+	  *found |= TLVFLAG_ROUTER_CAPABILITY;
+	  if (tlvs->router_capabilities == NULL)
+              tlvs->router_capabilities = list_new ();
+	  listnode_add (tlvs->router_capabilities, (pnt - 1));
+	  pnt += length;
+	  break;
+
+        case PORT_CAPABILITY:
+	  *found |= TLVFLAG_PORT_CAPABILITY;
+	  if (tlvs->port_capabilities == NULL)
+	      tlvs->port_capabilities  = list_new ();
+	  listnode_add (tlvs->port_capabilities, (pnt - 1));
+	  pnt += length;
+	  break;
+
 	default:
 	  zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d",
 		     areatag, type, length);
@@ -731,7 +749,8 @@ int
 add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
 {
 
-  if (STREAM_SIZE (stream) - stream_get_endp (stream) < (unsigned) len + 2)
+  if (STREAM_SIZE (stream) - stream_get_endp (stream) <
+      (unsigned) len + TLFLDS_LEN)
     {
       zlog_warn ("No room for TLV of type %d", tag);
       return ISIS_WARNING;
@@ -739,7 +758,8 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
 
   stream_putc (stream, tag);	/* TAG */
   stream_putc (stream, len);	/* LENGTH */
-  stream_put (stream, value, (int) len);	/* VALUE */
+  if (len > 0)
+    stream_put (stream, value, (int) len);	/* VALUE */
 
 #ifdef EXTREME_DEBUG
   zlog_debug ("Added TLV %d len %d", tag, len);
@@ -747,6 +767,52 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
   return ISIS_OK;
 }
 
+/*
+ * Add a subTLV to an existing TLV.  Returns ISIS_ERROR if it can't fit in the
+ * stream at all.  Returns ISIS_WARNING if it doesn't fit in the current TLV
+ * (but may fit in another one).
+ */
+int
+add_subtlv (u_char tag, u_char len, u_char * value, size_t tlvpos,
+    struct stream *stream)
+{
+  unsigned newlen;
+
+  /* Compute new outer TLV length */
+  newlen = stream_getc_from(stream, tlvpos + 1) + (unsigned) len + TLFLDS_LEN;
+
+  /* Check if it's possible to fit the subTLV in the stream at all */
+  if (STREAM_SIZE (stream) - stream_get_endp (stream) <
+      (unsigned) len + TLFLDS_LEN ||
+      len > 255 - TLFLDS_LEN)
+    {
+      zlog_debug ("No room for subTLV %d len %d", tag, len);
+      return ISIS_ERROR;
+    }
+
+  /* Check if it'll fit in the current TLV */
+  if (newlen > 255)
+    {
+#ifdef EXTREME_DEBUG
+      /* extreme debug only, because repeating TLV is usually possible */
+      zlog_debug ("No room for subTLV %d len %d in TLV %d", tag, len,
+		  stream_getc_from(stream, tlvpos));
+#endif /* EXTREME DEBUG */
+      return ISIS_WARNING;
+    }
+
+  stream_putc (stream, tag);	/* TAG */
+  stream_putc (stream, len);	/* LENGTH */
+  stream_put (stream, value, (int) len);	/* VALUE */
+  stream_putc_at (stream,  tlvpos + 1, newlen);
+
+#ifdef EXTREME_DEBUG
+  zlog_debug ("Added subTLV %d len %d to TLV %d", tag, len,
+	      stream_getc_from(stream, tlvpos));
+#endif /* EXTREME DEBUG */
+  return ISIS_OK;
+}
+
 int
 tlv_add_area_addrs (struct list *area_addrs, struct stream *stream)
 {
diff --git isisd/isis_tlv.h isisd/isis_tlv.h
index fc9f35f..f421627 100644
--- isisd/isis_tlv.h
+++ isisd/isis_tlv.h
@@ -60,6 +60,7 @@
  * P2P Adjacency State      240   y   n   n  RFC3373
  * IIH Sequence Number      241   y   n   n  draft-shen-isis-iih-sequence
  * Router Capability        242   -   -   -  draft-ietf-isis-caps
+ * Port Capability	    243   n   y   n  draft-eastlake-trill-bridge-isis
  *
  * 
  * IS Reachability sub-TLVs we (should) support.
@@ -85,6 +86,28 @@
  * 32bit administrative tag           1   draft-ietf-isis-admin-tags
  * 64bit administrative tag           2   draft-ietf-isis-admin-tags
  * Management prefix color          117   draft-ietf-isis-wg-multi-topology
+ *
+ *
+ * Router Capability sub-TLVs we support (Values TBD, temporary for now).
+ * ____________________________________________________________________________
+ * Name                           Value   Status
+ * ____________________________________________________________________________
+ * TRILL Flags			     21	  draft-ietf-trill-rbridge-protocol
+ * TRILL Nickname and Tree Root	     22	  draft-ietf-trill-rbridge-protocol
+ * TRILL Distribution Tree Roots     23	  draft-ietf-trill-rbridge-protocol
+ * TRILL VLANs and Bridge Roots	     24	  draft-ietf-trill-rbridge-protocol
+ * TRILL ESADI Participation	     25	  draft-ietf-trill-rbridge-protocol
+ * TRILL VLAN Groups		     26	  draft-ietf-trill-rbridge-protocol
+ * TRILL VLAN Mapping		     27	  draft-ietf-trill-rbridge-protocol
+ *
+ *
+ * Port Capability sub-TLVs we support
+ * ____________________________________________________________________________
+ * Name                           Value   Status
+ * ____________________________________________________________________________
+ * TRILL Special VLANs and Flags     10	  draft-ietf-trill-rbridge-protocol
+ * TRILL Enabled VLANs		     11	  draft-ietf-trill-rbridge-protocol
+ * TRILL Appointed Forwarders	     12	  draft-ietf-trill-rbridge-protocol
  */
 
 #define AREA_ADDRESSES            1
@@ -109,12 +132,44 @@
 #define IPV6_ADDR                 232
 #define IPV6_REACHABILITY         236
 #define WAY3_HELLO                240
+#define ROUTER_CAPABILITY	  242
+#define PORT_CAPABILITY		  243   /* TBD TRILL port capability TLV */
+
+/* ROUTER_CAPABILITY sub-TLVs for TRILL */
+#define	RCSTLV_TRILL_FLAGS	  21	/* TBD Flags */
+#define RCSTLV_TRILL_NICKNAME	  22	/* TBD Nickname and Tree Root */
+#define RCSTLV_TRILL_TREE_ROOTS	  23	/* TBD Distribution Tree Roots */
+#define RCSTLV_TRILL_VLANSROOTS	  24	/* TBD VLANs and Bridge Roots */
+#define RCSTLV_TRILL_ESADI	  25	/* TBD ESADI Participation */
+#define RCSTLV_TRILL_VLANGROUPS	  26	/* TBD VLAN Groups */
+#define RCSTLV_TRILL_VLANMAPPING  27	/* TBD VLAN Mapping */
+
+/* PORT_CAPABILITY sub-TLVs for TRILL */
+#define PCSTLV_VLANS		  10	/* Special VLANs and Flags */
+#define PCSTLV_ENABLEDVLANS	  11	/* Enabled VLANs */
+#define PCSTLV_APPFORWARDERS	  12	/* Appointed Forwarders */
 
 #define IS_NEIGHBOURS_LEN (ISIS_SYS_ID_LEN + 5)
 #define LAN_NEIGHBOURS_LEN 6
 #define LSP_ENTRIES_LEN (10 + ISIS_SYS_ID_LEN)	/* FIXME: should be entry */
 #define IPV4_REACH_LEN 12
 #define IPV6_REACH_LEN 22
+#define TLFLDS_LEN 2			         /* Length of Type & Len 8-bit fields */	
+#define ROUTER_CAPABILITY_MIN_LEN  5		 /* Min len of router capability TLV */
+#define ROUTER_CAPABILITY_MAX_LEN  250 		 /* Max len of router capability TLV */
+
+/* TRILL Flags sub-TLV */
+#define TRILL_FLAGS_SUBTLV_MIN_LEN 1 		 /* Len of sub-TLV val */
+#define	TRILL_FLAGS_V0	0x80
+#define	TRILL_FLAGS_V1	0x40
+#define	TRILL_FLAGS_V2	0x20
+#define	TRILL_FLAGS_V3	0x10
+
+#define TRILL_NICKNAME_SUBTLV_MIN_LEN 7 	 /* Len of TRILL nickname sub-TLV value field */
+#define TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN 4    /* Len of variable len TRILL VLANs and Bridge Roots sub-TLV value field */
+#define PCSTLV_VLANS_LEN	 4		 /* Exact len of port capability VLANs sub-TLV */
+#define PCSTLV_VLANFWDERS_MIN_LEN 6		 /* Min. len of each appointed forwarders sub-TLV */
+#define PCSTLV_ENABLEDVLANS_MIN_LEN 3		 /* Min. len of enabled VLANS sub-TLV */
 
 /* struct for neighbor */
 struct is_neigh
@@ -131,6 +186,15 @@ struct te_is_neigh
   u_char sub_tlvs_length;
 };
 
+/* Decode and encode three-octet metric into host byte order integer */
+#define GET_TE_METRIC(t) \
+  (((unsigned)(t)->te_metric[0]<<16) | ((t)->te_metric[1]<<8) | \
+   (t)->te_metric[2])
+#define SET_TE_METRIC(t, m) \
+  (((t)->te_metric[0] = (m) >> 16), \
+   ((t)->te_metric[1] = (m) >> 8), \
+   ((t)->te_metric[2] = (m)))
+
 /* struct for es neighbors */
 struct es_neigh
 {
@@ -213,7 +277,6 @@ struct ipv6_reachability
   u_char prefix_len;
   u_char prefix[16];
 };
-#endif /* HAVE_IPV6 */
 
 /* bits in control_info */
 #define CTRL_INFO_DIRECTION    0x80
@@ -223,6 +286,92 @@ struct ipv6_reachability
 #define DISTRIBUTION_INTERNAL  0
 #define DISTRIBUTION_EXTERNAL  1
 #define CTRL_INFO_SUBTLVS      0x20
+#endif /* HAVE_IPV6 */
+
+/* internal trill nickname struct */
+struct trill_nickname
+{
+  u_int16_t name;		/* network byte order */
+  u_int8_t priority;
+};
+
+/* Router Capability TLV: used in LSPs */
+struct router_capability_tlv
+{
+  u_char router_id[4];		   /* 4 octet Router ID */
+  u_int8_t flags;		   /* 1 octet flags */
+};
+
+/* internal router capability struct, includes tlv length */
+struct router_capability
+{
+  u_int8_t len;  		/* total length of the TLV */
+  struct router_capability_tlv rt_cap_tlv;
+};
+
+/* Port Capability TLV: used in Hellos */
+struct port_capability_tlv
+{
+  u_int8_t len;
+  u_int8_t value[1];
+};
+
+#ifdef __SUNPRO_C
+#pragma pack(1)
+#endif
+
+/* LSP: ROUTER_CAPABILITY RCSTLV_TRILL_NICKNAME */
+struct trill_nickname_subtlv
+{
+    u_int8_t tn_priority;
+    u_int16_t tn_nickname;
+    u_int16_t tn_trootpri;
+    u_int16_t tn_treecount;
+} __attribute__ ((packed));
+
+#ifdef __SUNPRO_C
+#pragma pack()
+#endif
+
+/* LSP: ROUTER_CAPABILITY RCSTLV_TRILL_VLANSROOTS */
+struct trill_vlan_bridge_roots_subtlv
+{
+    u_int16_t vlan_start;
+    u_int16_t vlan_end;
+};
+
+/* flags for vlan_start */
+#define	TVRFS_M4	0x8000
+#define	TVRFS_M6	0x4000
+#define	TVRFS_OM	0x2000
+#define	TVRFS_R		0x1000
+
+/* Hello: PORT_CAPABILITY PCSTLV_VLANS */
+struct trill_vlanflags_subtlv
+{
+    u_int16_t outer_vlan;
+    u_int16_t desig_vlan;
+};
+
+/* flags for outer_vlan */
+#define	TVFFO_AF	0x8000
+#define	TVFFO_AC	0x4000
+#define	TVFFO_VM	0x2000
+#define	TVFFO_R		0x1000
+
+/* Hello: PORT_CAPABILITY PCSTLV_APPFORWARDERS */
+struct appointed_vlanfwder_subtlv
+{
+    u_int16_t appointee_nick;
+    u_int16_t vlan_start;
+    u_int16_t vlan_end;
+};
+
+/* Hello: PORT_CAPABILITY PCSTLV_ENABLEDVLANS */
+struct trill_enabledvlans_subtlv
+{
+    u_int16_t start_vlan;
+};
 
 /*
  * Pointer to each tlv type, filled by parse_tlvs()
@@ -249,6 +398,8 @@ struct tlvs
   struct list *ipv6_reachs;
 #endif
   struct isis_passwd auth_info;
+  struct list *router_capabilities;
+  struct list *port_capabilities;
 };
 
 /*
@@ -277,6 +428,8 @@ struct tlvs
 #define TLVFLAG_TE_ROUTER_ID              (1<<19)
 #define TLVFLAG_CHECKSUM                  (1<<20)
 #define TLVFLAG_GRACEFUL_RESTART          (1<<21)
+#define TLVFLAG_ROUTER_CAPABILITY         (1<<22)
+#define TLVFLAG_PORT_CAPABILITY	          (1<<23)
 
 void init_tlvs (struct tlvs *tlvs, uint32_t expected);
 void free_tlvs (struct tlvs *tlvs);
@@ -284,6 +437,11 @@ int parse_tlvs (char *areatag, u_char * stream, int size,
 		u_int32_t * expected, u_int32_t * found, struct tlvs *tlvs);
 void free_tlv (void *val);
 
+int add_tlv (u_char, u_char, u_char *, struct stream *);
+int add_subtlv (u_char, u_char, u_char *, size_t, struct stream *);
+
+int tlv_add_trill_nickname (struct trill_nickname *nick_info, struct stream *stream,
+		struct isis_area *area);
 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);
@@ -304,6 +462,7 @@ int tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream);
 int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream);
 #endif /* HAVE_IPV6 */
 
+int tlv_add_trill_vlans(struct isis_circuit *);
 int tlv_add_padding (struct stream *stream);
 
 #endif /* _ZEBRA_ISIS_TLV_H */
diff --git isisd/isis_trill.c isisd/isis_trill.c
new file mode 100644
index 0000000..3a38660
--- /dev/null
+++ isisd/isis_trill.c
@@ -0,0 +1,2346 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_trill.c
+ *
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ *
+ * 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 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <zebra.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdlbridge.h>
+#include <libdlvlan.h>
+#include <net/trill.h>
+
+#include "thread.h"
+#include "linklist.h"
+#include "stream.h"
+#include "vty.h"
+#include "log.h"
+#include "command.h"
+#include "memory.h"
+#include "prefix.h"
+#include "hash.h"
+#include "if.h"
+#include "table.h"
+#include "privs.h"
+
+#include "isisd/dict.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_events.h"
+#include "isisd/bool.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_csm.h"
+
+extern struct zebra_privs_t isisd_privs;
+
+/* Number of available (randomly-assigned) nicknames, not counting reserved */
+static int nickavailcnt;
+
+/* Vector with bits set to indicate nicknames in use */
+static u_char nickbitvector[NICKNAMES_BITARRAY_SIZE];
+#define	NICK_IS_USED(n)		(nickbitvector[(n)/8] & (1<<((n)%8)))
+#define	NICK_SET_USED(n)	(nickbitvector[(n)/8] |= (1<<((n)%8)))
+#define	NICK_CLR_USED(n)	(nickbitvector[(n)/8] &= ~(1<<((n)%8)))
+
+/* Number of zero bits in each word of vector */
+static u_char clear_bit_count[CLEAR_BITARRAY_SIZE];
+
+static dladm_handle_t dlhandle;
+static char cfile_present = TRUE;
+
+static nickdb_search_result trill_search_rbridge (struct isis_area *, nickinfo_t *, dnode_t **);
+static void trill_dict_delete_nodes (dict_t *, dict_t *, void *, int);
+static int trill_nick_conflict(nickinfo_t *, nickinfo_t *);
+static int trill_parse_lsp (struct isis_lsp *, nickinfo_t *);
+
+/* Test and mark a nickname in host byte order as allocated or free */
+static int
+trill_nickname_nickbitmap_op(u_int16_t nick, int update, int val)
+{
+  if (nick == RBRIDGE_NICKNAME_NONE || nick == RBRIDGE_NICKNAME_UNUSED)
+    return FALSE;
+  if (val)
+    {
+      if (NICK_IS_USED(nick))
+	return TRUE;
+      if (!update)
+	return FALSE;
+      NICK_SET_USED(nick);
+      if (nick < RBRIDGE_NICKNAME_MINRES)
+	nickavailcnt--;
+      clear_bit_count[nick / CLEAR_BITARRAY_ENTRYLENBITS]--;
+    }
+  else
+    {
+      if (!NICK_IS_USED(nick))
+	return TRUE;
+      if (!update)
+	return FALSE;
+      NICK_CLR_USED(nick);
+      if (nick < RBRIDGE_NICKNAME_MINRES)
+	nickavailcnt++;
+      clear_bit_count[nick / CLEAR_BITARRAY_ENTRYLENBITS]++;
+    }
+  return FALSE;
+}
+
+/*
+ * trill_nickname_gen calls this function to randomly allocate a new nickname
+ * in host byte order. We also keep track of allocated and in-use nicks.
+ */
+static u_int16_t
+trill_nickname_alloc(void)
+{
+  u_int i, j, k;
+  u_int16_t nick;
+  u_int nicknum;
+  u_int freenickcnt = 0;
+
+  if (nickavailcnt < 1)
+    return RBRIDGE_NICKNAME_NONE;
+ 
+  /*
+   * Note that rand() usually returns 15 bits, so we overlap two values to make
+   * sure we're getting at least 16 bits (as long as rand() returns 8 bits or
+   * more).  Using random() instead would be better, but isis_main.c uses
+   * srand.
+   */
+  nicknum = ((rand() << 8) | rand()) % nickavailcnt;
+  for ( i = 0; i < sizeof (clear_bit_count); i++ )
+    {
+      freenickcnt += clear_bit_count[i];
+      if (freenickcnt <= nicknum)
+        continue;
+      nicknum -= freenickcnt - clear_bit_count[i];
+      nick = i * CLEAR_BITARRAY_ENTRYLEN * 8;
+      for ( j = 0; j < CLEAR_BITARRAY_ENTRYLEN; j++)
+	{
+	   for (k = 0; k < 8; k++, nick++)
+	     {
+		if (!NICK_IS_USED(nick) && nicknum-- == 0)
+		  {
+		    trill_nickname_nickbitmap_op (nick, TRUE, TRUE);
+		    return nick;
+		  }
+	     }
+	}
+      break;
+    }
+  assert (0);
+  return 0;
+}
+
+static void trill_nickname_reserve(u_int16_t nick_nbo)
+{
+  trill_nickname_nickbitmap_op(ntohs(nick_nbo), TRUE, TRUE);
+}
+
+static int is_nickname_used(u_int16_t nick_nbo)
+{
+  return trill_nickname_nickbitmap_op(ntohs(nick_nbo), FALSE, TRUE);
+}
+
+static void trill_nickname_free(u_int16_t nick_nbo)
+{
+  trill_nickname_nickbitmap_op(ntohs(nick_nbo), TRUE, FALSE);
+}
+
+static void
+trill_nickname_gen(struct isis_area *area)
+{
+  u_int16_t nick;
+
+  nick = trill_nickname_alloc();
+  if (nick == RBRIDGE_NICKNAME_NONE)
+    {
+      zlog_err("RBridge nickname allocation failed.  No nicknames available.");
+      abort();
+    }
+  else
+    {
+      area->trill->nick.name = htons(nick);
+      dladm_bridge_set_nick(area->trill->name, nick);
+      if (isis->debugs & DEBUG_TRILL_EVENTS)
+	zlog_debug("ISIS TRILL generated nick:%u", nick);
+    }
+}
+
+static int
+nick_cmp(const void *key1, const void *key2)
+{
+  return (memcmp(key1, key2, sizeof(u_int16_t)));
+}
+
+static int
+sysid_cmp(const void *key1, const void *key2)
+{
+  return (memcmp(key1, key2, ISIS_SYS_ID_LEN));
+}
+
+void
+trill_area_init(struct isis_area *area)
+{
+  u_int i;
+
+  area->trill->status = 0;
+  area->trill->nick.priority = DFLT_NICK_PRIORITY;
+  area->trill->root_priority = TRILL_DFLT_ROOT_PRIORITY;
+  area->trill->nickdb = dict_create(MAX_RBRIDGE_NODES, nick_cmp);
+  area->trill->sysidtonickdb = dict_create(MAX_RBRIDGE_NODES, sysid_cmp);
+
+  nickavailcnt = RBRIDGE_NICKNAME_MINRES - RBRIDGE_NICKNAME_NONE - 1;
+  memset(nickbitvector, 0, sizeof(nickbitvector));
+  for (i = 0; i < sizeof (clear_bit_count); i++)
+    clear_bit_count[i] = CLEAR_BITARRAY_ENTRYLENBITS;
+
+  /* These two are always reserved */
+  NICK_SET_USED(RBRIDGE_NICKNAME_NONE);
+  NICK_SET_USED(RBRIDGE_NICKNAME_UNUSED);
+  clear_bit_count[RBRIDGE_NICKNAME_NONE / CLEAR_BITARRAY_ENTRYLENBITS]--;
+  clear_bit_count[RBRIDGE_NICKNAME_UNUSED / CLEAR_BITARRAY_ENTRYLENBITS]--;
+
+  isis_event_system_type_change (area, TRILL_ISIS_LEVEL);
+  memset (area->trill->lspdb_acq_reqs, 0, sizeof(area->trill->lspdb_acq_reqs));
+}
+
+/*
+ * Called from isisd to handle trill nickname command.
+ * Nickname is user configured and in host byte order
+ */
+int
+trill_area_nickname(struct isis_area *area, u_int16_t nickname)
+{
+  int savednick;
+
+  if (nickname == RBRIDGE_NICKNAME_NONE)
+    {
+      /* Called from "no trill nickname" command */
+      trill_nickname_gen (area);
+      SET_FLAG (area->trill->status, TRILL_NICK_SET);
+      SET_FLAG (area->trill->status, TRILL_AUTONICK);
+      lsp_regenerate_schedule (area);
+      return TRUE;
+    }
+
+  nickname = htons(nickname);
+  savednick = area->trill->nick.name;
+  area->trill->nick.name = nickname;
+  area->trill->nick.priority |= CONFIGURED_NICK_PRIORITY;
+
+  /*
+   * Check if we know of another RBridge already using this nickname.
+   * If yes check if it conflicts with the nickname in the database.
+   */
+  if (is_nickname_used(nickname))
+    {
+      nickinfo_t ni;
+      dnode_t *dnode;
+      nicknode_t *tnode;
+
+      ni.nick = area->trill->nick;
+      memcpy(ni.sysid, isis->sysid, ISIS_SYS_ID_LEN);
+      if (trill_search_rbridge (area, &ni, &dnode) == FOUND)
+        {
+          assert (dnode);
+          tnode = dnode_get (dnode);
+          if (trill_nick_conflict (&(tnode->info), &ni))
+            {
+              trill_dict_delete_nodes (area->trill->nickdb,
+		     area->trill->sysidtonickdb, &nickname, FALSE);
+	    }
+	  else
+	    {
+              /* 
+	       * The other nick in our nickdb has greater priority so return
+	       * fail, restore nick and let user configure another nick.
+	       */
+               area->trill->nick.name = savednick; 
+	       area->trill->nick.priority &= ~CONFIGURED_NICK_PRIORITY;
+               return FALSE;
+	    }
+	}
+    }
+
+  trill_nickname_reserve(nickname);
+  SET_FLAG(area->trill->status, TRILL_NICK_SET);
+  UNSET_FLAG(area->trill->status, TRILL_AUTONICK);
+  lsp_regenerate_schedule (area);
+  return TRUE;
+}
+
+static void
+trill_nickname_priority_update(struct isis_area *area, u_int8_t priority)
+{
+  if (priority)
+    {
+      area->trill->nick.priority = priority;
+      SET_FLAG(area->trill->status, TRILL_PRIORITY_SET);
+    }
+  else
+    {
+      /* Called from "no trill nickname priority" command */
+      area->trill->nick.priority = DFLT_NICK_PRIORITY;
+      UNSET_FLAG(area->trill->status, TRILL_PRIORITY_SET);
+    }
+
+  /*
+   * Set the configured nickname priority bit if the
+   * nickname was not automatically generated. 
+   */
+  if (!CHECK_FLAG(area->trill->status, TRILL_AUTONICK))
+     area->trill->nick.priority |= CONFIGURED_NICK_PRIORITY;
+  lsp_regenerate_schedule (area);
+} 
+
+static void
+trill_nickinfo_del(nickinfo_t *ni)
+{
+  if (ni->dt_roots != NULL)
+    list_delete (ni->dt_roots);
+  if (ni->broots != NULL)
+    list_delete (ni->broots);
+}
+
+static void
+trill_dict_remnode ( dict_t *dict, dnode_t *dnode)
+{
+  nicknode_t *tnode;
+
+  assert (dnode);
+  tnode = dnode_get (dnode);
+  assert(tnode->refcnt);
+  tnode->refcnt--;
+  if (tnode->refcnt == 0)
+    {
+      isis_spftree_del (tnode->rdtree);
+      trill_nickinfo_del (&tnode->info);
+      if (tnode->adjnodes)
+        list_delete (tnode->adjnodes);
+      if (tnode->vlans_reachable)
+        list_delete (tnode->vlans_reachable);
+      XFREE (MTYPE_ISIS_TRILL_NICKDB_NODE, tnode);
+    }
+  dict_delete_free (dict, dnode);
+}
+
+static void
+trill_dict_free (dict_t *dict)
+{
+  dnode_t *dnode, *next;
+
+  dnode = dict_first (dict);
+  while (dnode)
+    {
+      next = dict_next (dict, dnode);
+      trill_dict_remnode (dict, dnode);
+      dnode = next;
+    }
+  dict_free_nodes (dict);
+  dict_destroy (dict);
+}
+
+void
+trill_area_free(struct isis_area *area)
+{
+  area->trill->status = 0;
+  trill_dict_free (area->trill->nickdb);
+  trill_dict_free (area->trill->sysidtonickdb);
+  if (area->trill->fwdtbl)
+    list_delete (area->trill->fwdtbl);
+  if (area->trill->adjnodes)
+    list_delete (area->trill->adjnodes);
+  if (area->trill->dt_roots)
+    list_delete (area->trill->dt_roots);
+  if (area->trill->vlans_reachable)
+    list_delete (area->trill->vlans_reachable);
+}
+
+/* 
+ * Delete nickname node in both databases. First a lookup
+ * of the node in first db by key1 and using the found node
+ * a lookup of the node in second db is done. Asserts the
+ * node if exists in one also exist in the second db.
+ */
+static void
+trill_dict_delete_nodes (dict_t *dict1, dict_t *dict2,
+		void *key1, int key2isnick)
+{
+  dnode_t *dnode1;
+  dnode_t *dnode2;
+  nicknode_t *tnode;
+  int nickname;
+
+  dnode1 = dict_lookup (dict1, key1);
+  if (dnode1)
+    {
+      tnode = (nicknode_t *) dnode_get(dnode1);
+      if (tnode)
+        {
+          if (key2isnick)
+	    {
+              dnode2 = dict_lookup (dict2, &(tnode->info.nick.name));
+              nickname = tnode->info.nick.name;
+	    }
+          else 
+            {
+              dnode2 = dict_lookup (dict2, tnode->info.sysid);
+	      nickname = *(int *)key1;
+	    }
+	  assert (dnode2);
+          trill_dict_remnode (dict2, dnode2);
+
+	  /* Mark the nickname as available */
+	  trill_nickname_free(nickname);
+	}
+      trill_dict_remnode (dict1, dnode1);
+    }
+}
+
+static void
+trill_update_nickinfo (nicknode_t *tnode, nickinfo_t *recvd_nick)
+{
+  trill_nickinfo_del(&tnode->info);
+  tnode->info = *recvd_nick;
+  /* clear copied nick */
+  memset(recvd_nick, 0, sizeof (*recvd_nick));
+}
+
+static void
+trill_dict_create_nodes (struct isis_area *area, nickinfo_t *nick)
+{
+  nicknode_t *tnode;
+
+  tnode = XCALLOC (MTYPE_ISIS_TRILL_NICKDB_NODE, sizeof(nicknode_t));
+  tnode->info = *nick;
+  dict_alloc_insert (area->trill->nickdb, &(tnode->info.nick.name), tnode);
+  tnode->refcnt = 1;
+  dict_alloc_insert (area->trill->sysidtonickdb, tnode->info.sysid, tnode);
+  tnode->refcnt++;
+  /* Mark the nickname as reserved */
+  trill_nickname_reserve(nick->nick.name);
+  tnode->rdtree = isis_spftree_new();
+  /* clear copied nick */
+  memset(nick, 0, sizeof (*nick));
+}
+
+/*
+ * Update nickname information in the dictionary objects.
+ */
+static void
+trill_nickdb_update ( struct isis_area *area, nickinfo_t *newnick)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+  nickdb_search_result res;
+
+  res = trill_search_rbridge (area, newnick, &dnode);
+  if (res == NOTFOUND) 
+    {
+      trill_dict_create_nodes (area, newnick);
+      return;
+    }
+
+  assert (dnode);
+  tnode = dnode_get (dnode);
+
+  /* If nickname & system ID of the node in our database match
+   * the nick received then we don't have to change any dictionary
+   * nodes. Update only the node information. Otherwise we update
+   * the dictionary nodes.
+   */
+  if (res == DUPLICATE || res == PRIORITY_CHANGE_ONLY)
+    {
+      trill_update_nickinfo (tnode, newnick);
+      return;
+    }
+
+  /*
+   * If the RBridge has a new nick then update its nick only.
+   */
+  if (res == NICK_CHANGED) 
+    {
+      if (isis->debugs & DEBUG_TRILL_EVENTS)
+        zlog_debug("ISIS TRILL storing new nick:%d from sysID:%s",
+	   ntohs(tnode->info.nick.name), sysid_print(tnode->info.sysid));
+
+      /* Delete the current nick in from our database */
+      trill_dict_delete_nodes (area->trill->sysidtonickdb,
+	      area->trill->nickdb, tnode->info.sysid, TRUE);
+      /* Store the new nick entry */
+      trill_dict_create_nodes (area, newnick);
+    }
+  else
+    {
+      /*
+       * There is another RBridge using the same nick.
+       * Determine which of the two RBridges should use the nick.
+       * But first we should delete any prev nick associated
+       * with system ID sending the newnick as it has just
+       * announced a new nick.
+       */
+      trill_dict_delete_nodes (area->trill->sysidtonickdb,
+	      area->trill->nickdb, newnick->sysid, TRUE);
+
+      if (trill_nick_conflict (&(tnode->info), newnick))
+        {
+          /* 
+	   * RBridge in tnode should choose another nick.
+	   * Delete tnode from our nickdb and store newnick.
+	   */
+          if (isis->debugs & DEBUG_TRILL_EVENTS)
+	    {
+              zlog_debug("ISIS TRILL replacing conflict nick:%d of sysID:%s",
+	        ntohs(tnode->info.nick.name), sysid_print(tnode->info.sysid));
+	      zlog_debug("ISIS TRILL .....with nick:%d of sysID:%s", 
+		ntohs(newnick->nick.name), sysid_print(newnick->sysid));
+	    }
+
+           trill_dict_delete_nodes (area->trill->sysidtonickdb,
+	      area->trill->nickdb, tnode->info.sysid, TRUE);
+           trill_dict_create_nodes (area, newnick);
+        }
+      else if (isis->debugs & DEBUG_TRILL_EVENTS)
+        {
+          zlog_debug("ISIS TRILL updated nick:%d of sysID:%s not accepted",
+		ntohs(newnick->nick.name), sysid_print(newnick->sysid));
+          zlog_debug("ISIS TRILL because of conflict with existing nick:%d of sysID:%s",
+		ntohs(tnode->info.nick.name), sysid_print(tnode->info.sysid));
+        }
+    }
+}
+
+/* 
+ * Search the nickname database and the sysidtonick database
+ * to see if we know a rbridge that matches either the passed nickname 
+ * or system ID or both. 
+ */
+static nickdb_search_result
+trill_search_rbridge ( struct isis_area *area, nickinfo_t *ni,
+		dnode_t **fndnode)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+
+  dnode = dict_lookup (area->trill->nickdb, &(ni->nick.name));
+  if (dnode == NULL) 
+    dnode = dict_lookup(area->trill->sysidtonickdb, ni->sysid);
+  if (dnode == NULL)
+    return NOTFOUND;
+
+  tnode = (nicknode_t *) dnode_get (dnode);
+  assert (tnode != NULL);
+  assert (tnode->refcnt);
+
+  if (fndnode)
+    *fndnode = dnode;
+  if ( memcmp(&(tnode->info.sysid), ni->sysid, ISIS_SYS_ID_LEN) != 0)
+    return FOUND;
+  if (tnode->info.nick.name != ni->nick.name)
+    return NICK_CHANGED;
+  if (tnode->info.nick.priority != ni->nick.priority)
+    return PRIORITY_CHANGE_ONLY;
+  /* Exact nick and sysid match */
+  return DUPLICATE; 
+}
+
+/*
+ * trill_nick_conflict: nickname conflict resolution fn
+ * Returns FALSE when nick1 has greater priority and
+ * returns TRUE when nick1 has lower priority and 
+ * must be changed.
+ */
+static int
+trill_nick_conflict(nickinfo_t *nick1, nickinfo_t *nick2)
+{
+  assert (nick1->nick.name == nick2->nick.name);
+
+  /* If nick1 priority is greater (or) 
+   * If priorities match & nick1 sysid is greater 
+   * then nick1 has higher priority
+   */
+  if ((nick1->nick.priority > nick2->nick.priority) ||
+      (nick1->nick.priority == nick2->nick.priority && 
+       (sysid_cmp (nick1->sysid, nick2->sysid) > 0)))
+    return FALSE;
+
+  return TRUE;
+}
+
+/*
+ * Remove nickname from the database.
+ * Called from lsp_destroy or when lsp is missing a nickname TLV.
+ */
+void
+trill_nick_destroy(struct isis_lsp *lsp)
+{
+  u_char *lsp_id;
+  nickinfo_t ni;
+  struct isis_area *area;
+  int delnick;
+
+  if (!isis->trill_active)
+    return;
+
+  area = listgetdata(listhead (isis->area_list));
+  lsp_id = lsp->lsp_header->lsp_id;
+
+  /* 
+   * If LSP is our own or is a Pseudonode LSP (and we do not
+   * learn nicks from Pseudonode LSPs) then no action is needed.
+   */
+  if ((memcmp (lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
+       || (LSP_PSEUDO_ID(lsp_id) != 0))
+    return;
+
+  if (!trill_parse_lsp (lsp, &ni) ||
+	  (ni.nick.name == RBRIDGE_NICKNAME_NONE))
+    {
+      /* Delete the nickname associated with the LSP system ID
+       * (if any) that did not include router capability TLV or
+       * TRILL flags or the nickname in the LSP is unknown. This
+       * happens when we recv a LSP from RBridge that just re-started
+       * and we have to delete the prev nick associated with it.
+       */
+      trill_dict_delete_nodes (area->trill->sysidtonickdb,
+	      area->trill->nickdb, lsp_id, TRUE);
+      if (isis->debugs & DEBUG_TRILL_EVENTS)
+        zlog_debug("ISIS TRILL removed any nickname associated"
+	      " with sysID:%s LSP seqnum:0x%08x pseudonode:%x",
+	      sysid_print(lsp_id), ntohl (lsp->lsp_header->seq_num),
+	      LSP_PSEUDO_ID(lsp_id) );
+      trill_nickinfo_del (&ni);
+      return;
+    }
+      
+  memcpy(ni.sysid, lsp_id, ISIS_SYS_ID_LEN);
+  delnick = ntohs(ni.nick.name);
+  if (delnick != RBRIDGE_NICKNAME_NONE &&
+      delnick != RBRIDGE_NICKNAME_UNUSED &&
+      ni.nick.priority >= MIN_RBRIDGE_PRIORITY)
+    {
+      /* Only delete if the nickname was learned
+       * from the LSP by ensuring both system ID
+       * and nickname in the LSP match with a node
+       * in our nick database.
+       */
+      if (trill_search_rbridge (area, &ni, NULL) == DUPLICATE)
+        {
+           trill_dict_delete_nodes (area->trill->sysidtonickdb,
+	      area->trill->nickdb, ni.sysid, TRUE);
+           if (isis->debugs & DEBUG_TRILL_EVENTS)
+              zlog_debug("ISIS TRILL removed nickname:%d associated"
+		" with sysID:%s LSP ID:0x%08x pseudonode:%x",
+		delnick, sysid_print(lsp_id),
+		ntohl (lsp->lsp_header->seq_num),
+	        LSP_PSEUDO_ID(lsp_id) );
+	}
+    }
+  else if (isis->debugs & DEBUG_TRILL_EVENTS)
+    zlog_debug("ISIS TRILL nick destroy invalid nickname:%d"
+        " from sysID:%s", delnick, sysid_print(lsp_id) );
+  trill_nickinfo_del (&ni);
+}
+
+static void
+trill_nick_recv(struct isis_area *area, nickinfo_t *other_nick)
+{
+  nickinfo_t ournick;
+  int nickchange = FALSE;
+
+  ournick.nick = area->trill->nick;
+  memcpy (ournick.sysid, area->isis->sysid, ISIS_SYS_ID_LEN);
+
+  if (isis->debugs & DEBUG_TRILL_EVENTS)
+    zlog_debug("ISIS TRILL nick recv:%d from sysID:%s",
+      ntohs (other_nick->nick.name), sysid_print(other_nick->sysid) );
+
+  /* Check for reserved TRILL nicknames that are not valid for use */
+  if ((other_nick->nick.name == RBRIDGE_NICKNAME_NONE) ||
+	  (other_nick->nick.name == RBRIDGE_NICKNAME_UNUSED)) 
+    {
+       zlog_warn("ISIS TRILL received reserved nickname:%d from sysID:%s",
+          ntohs (other_nick->nick.name), sysid_print(other_nick->sysid) );
+       return;
+    }
+
+  if (!(other_nick->flags & TRILL_FLAGS_V0))
+    {
+      zlog_info ("ISIS TRILL nick %d doesn't support V0 headers; flags %02X",
+	  ntohs (other_nick->nick.name), other_nick->flags);
+      return;
+    }
+
+  /* Check for conflict with our own nickname */
+  if (other_nick->nick.name == area->trill->nick.name)
+    {
+       /* Check if our nickname has lower priority or our
+	* system ID is lower, if not we keep our nickname.
+	*/
+       if (!(nickchange = trill_nick_conflict (&ournick, other_nick)))
+          return;
+    }
+
+  /* Update our nick database */
+  trill_nickdb_update (area, other_nick);
+
+  if (nickchange)
+     {
+       /* We choose another nickname */
+        trill_nickname_gen (area);
+        SET_FLAG(area->trill->status, TRILL_AUTONICK);
+
+	/* If previous nick was configured remove the bit 
+	 * indicating nickname was configured  (0x80) */
+	area->trill->nick.priority &= ~CONFIGURED_NICK_PRIORITY;
+
+	/* Regenerate our LSP to advertise the new nickname */
+	lsp_regenerate_schedule (area);
+
+        if (isis->debugs & DEBUG_TRILL_EVENTS)
+          zlog_debug("ISIS TRILL our nick changed to:%d",
+	    ntohs (area->trill->nick.name));
+     }
+}
+
+void
+trill_lspdb_acquire_event(struct isis_circuit *circuit, lspdbacq_state caller)
+{
+  struct isis_area *area;
+  u_int8_t cid;
+  struct listnode *cnode;
+  int done = TRUE;
+
+  area = circuit->area;
+  cid = circuit->circuit_id;
+
+  if (!isis->trill_active)
+    return;
+  if (CHECK_FLAG (area->trill->status, (TRILL_LSPDB_ACQUIRED | TRILL_NICK_SET)))
+    return;
+ 
+  switch(caller)
+    {
+    case CSNPRCV:
+    case CSNPSND:
+      LSPDB_ACQTRYINC (area, cid);
+      break;
+    case PSNPSNDTRY:
+      if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+        LSPDB_ACQTRYINC (area, cid);
+      break;
+    default:
+      break;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
+    {
+      cid = circuit->circuit_id;
+
+      /* 
+       * If on any circuit we have reached max tries
+       * we consider LSP DB acquisition as done and
+       * assign ourselves a nickname
+       */
+      if (LSPDB_ACQTRYVAL (area, cid) > MAX_LSPDB_ACQTRIES)
+        {
+          done = TRUE;
+	  break;
+        }
+
+      /* 
+       * If on any circuits we haven't received min LSPDB update
+       * packets then we wait until we hit max tries above
+       * on any circuit. If not it can only mean there is no other
+       * IS-IS instance on any of our circuits and so we wait.
+       */
+      if (LSPDB_ACQTRYVAL (area, cid) < MIN_LSPDB_ACQTRIES)
+        done = FALSE;
+    }
+
+  if (isis->debugs & DEBUG_TRILL_EVENTS)
+    zlog_debug("ISIS TRILL LSPDB acquire event:%d cid:%d, done:%d",
+	    caller, cid, done);
+
+  if (done)
+    {
+      /* 
+       * LSP DB acquired state, sufficient to start
+       * advertising our nickname. Set flags, pick a
+       * new nick if necessary and trigger new LSPs with the nick.
+       */
+      SET_FLAG (area->trill->status, TRILL_LSPDB_ACQUIRED);
+      if (ntohs(area->trill->nick.name) == RBRIDGE_NICKNAME_NONE)
+	{
+	  trill_nickname_gen (area);
+	  SET_FLAG (area->trill->status, TRILL_NICK_SET);
+	  SET_FLAG (area->trill->status, TRILL_AUTONICK);
+	  lsp_regenerate_schedule (area);
+	}
+    }
+}
+
+static void
+trill_del_broot_node(void *data)
+{
+  struct trill_vlan_bridge_roots *broot = data;
+  if (broot->bridge_roots != NULL)
+    XFREE (MTYPE_ISIS_TRILL_BRIDGE_ROOTIDS, broot->bridge_roots);
+  XFREE (MTYPE_ISIS_TRILL_VLANBRIDGE_ROOTS, broot);
+}
+
+/*
+ * Returns TRUE if a nickname was received in the parsed LSP
+ */
+static int
+trill_parse_lsp (struct isis_lsp *lsp, nickinfo_t *recvd_nick)
+{
+  struct listnode *node;
+  struct router_capability *rtr_cap;
+  struct trill_vlan_bridge_roots *broot;
+  struct trill_vlan_bridge_roots_subtlv *brootstlv;
+  u_int8_t subtlvs_len;
+  u_int8_t subtlv;
+  u_int8_t subtlv_len;
+  u_int8_t stlvlen;
+  u_int16_t dtroot_nick;
+  int nick_recvd = FALSE;
+  int flags_recvd = FALSE;
+  int broots_recvd = FALSE;
+  u_char *pnt;
+  int idx;
+
+  memset(recvd_nick, 0, sizeof(nickinfo_t));
+  if (lsp->tlv_data.router_capabilities == NULL)
+    return FALSE;
+
+  memcpy (recvd_nick->sysid, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN);
+  recvd_nick->root_priority = TRILL_DFLT_ROOT_PRIORITY;
+
+  for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.router_capabilities, node, rtr_cap))
+    {
+       if (rtr_cap->len < ROUTER_CAPABILITY_MIN_LEN)
+         continue;
+
+       subtlvs_len = rtr_cap->len - ROUTER_CAPABILITY_MIN_LEN;
+       pnt = ((u_char *)rtr_cap) + sizeof(struct router_capability);
+       while (subtlvs_len >= TLFLDS_LEN)
+         {
+           subtlv = *(u_int8_t *)pnt++; subtlvs_len--;
+	   subtlv_len = *(u_int8_t *)pnt++; subtlvs_len--;
+	   if (subtlv_len > subtlvs_len)
+	     {
+               zlog_warn("ISIS trill_parse_lsp received invalid router"
+	         " capability subtlvs_len:%d subtlv_len:%d", 
+		 subtlvs_len, subtlv_len);
+               break;
+	     }
+
+	   switch (subtlv)
+	     {
+	     case RCSTLV_TRILL_FLAGS:
+	       /* var. len with min. one octet and must be included in each link state PDU */
+	       if (!flags_recvd && subtlv_len >= TRILL_FLAGS_SUBTLV_MIN_LEN)
+                 {
+	           recvd_nick->flags = *(u_int8_t *)pnt;
+	           flags_recvd = TRUE;
+		 }
+	       else
+	         { 
+		   if (flags_recvd)
+                     zlog_warn("ISIS trill_parse_lsp multiple TRILL"
+				    " flags sub-TLVs received");
+		   else
+                     zlog_warn("ISIS trill_parse_lsp invalid len:%d"
+				    " of TRILL flags sub-TLV", subtlv_len);
+		 }
+	       pnt += subtlv_len;
+	       subtlvs_len -= subtlv_len;
+	       break;
+
+	     case RCSTLV_TRILL_NICKNAME:
+	       stlvlen = subtlv_len;
+	       if (!nick_recvd && subtlv_len >= TRILL_NICKNAME_SUBTLV_MIN_LEN)
+                 {
+		   struct trill_nickname_subtlv *tn;
+
+		   tn = (struct trill_nickname_subtlv *)pnt;
+                   recvd_nick->nick.priority = tn->tn_priority;
+	           recvd_nick->nick.name = tn->tn_nickname;
+		   recvd_nick->root_priority = ntohs(tn->tn_trootpri);
+		   recvd_nick->root_count = ntohs(tn->tn_treecount);
+	           nick_recvd = TRUE;
+		 }
+	       else
+	         { 
+		   if (nick_recvd)
+                     zlog_warn("ISIS trill_parse_lsp multiple TRILL"
+				    " nick sub-TLVs received");
+		   else
+                     zlog_warn("ISIS trill_parse_lsp invalid len:%d"
+				    " of TRILL nick sub-TLV", subtlv_len);
+		 }
+               pnt += stlvlen;
+               subtlvs_len -= subtlv_len;
+	       break;
+
+	     case RCSTLV_TRILL_TREE_ROOTS:
+               if (subtlv_len % TRILL_NICKNAME_LEN)
+                 {
+                   pnt += subtlv_len;
+		   subtlvs_len -= subtlv_len;
+                   zlog_warn("ISIS trill_parse_lsp received invalid"
+		     " distribution tree roots subtlv_len:%d", subtlv_len);
+		   break;
+		 }
+	       if (recvd_nick->dt_roots == NULL)
+                 recvd_nick->dt_roots = list_new();
+	       stlvlen = subtlv_len;  /* zero len possible */
+	       while (stlvlen > 0)
+	         {
+                   dtroot_nick = *(u_int16_t *)pnt;
+		   pnt += TRILL_NICKNAME_LEN;
+		   subtlvs_len -= TRILL_NICKNAME_LEN;
+		   stlvlen -= TRILL_NICKNAME_LEN;
+
+                   if (dtroot_nick == RBRIDGE_NICKNAME_NONE ||
+		       dtroot_nick == RBRIDGE_NICKNAME_UNUSED)
+		     {
+                       zlog_warn("ISIS trill_parse_lsp received invalid"
+			 " distribution tree root nick:%d.", dtroot_nick);
+                       continue;
+		     }
+		   listnode_add (recvd_nick->dt_roots, (void *)(u_long)*(u_int16_t *)pnt);
+	         }
+	       break;
+
+	     case RCSTLV_TRILL_VLANSROOTS:
+	       if (subtlv_len < TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN)
+                 {
+                   pnt += subtlv_len;
+		   subtlvs_len -= subtlv_len;
+                   zlog_warn("ISIS trill_parse_lsp received invalid"
+		     " vlans and bridge roots subtlv_len:%d", subtlv_len);
+		   break;
+		 }
+
+	       if (recvd_nick->broots == NULL)
+                 {
+                   recvd_nick->broots = list_new();
+		   recvd_nick->broots->del = trill_del_broot_node;
+		 }
+
+	       broot = XCALLOC (MTYPE_ISIS_TRILL_VLANBRIDGE_ROOTS,
+		       sizeof(struct trill_vlan_bridge_roots));
+	       brootstlv = (struct trill_vlan_bridge_roots_subtlv *)pnt;
+	       broot->vlan_start = VLANTCI(ntohs(brootstlv->vlan_start));
+	       broot->vlan_end = VLANTCI(ntohs(brootstlv->vlan_end));
+	       pnt += TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN;
+	       subtlvs_len -= TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN;
+	       subtlv_len -= TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN;
+	       if (subtlv_len % ETHERADDRL)
+                 {
+                   pnt += subtlv_len;
+		   subtlvs_len -= subtlv_len;
+                   zlog_warn("ISIS trill_parse_lsp received invalid"
+		     " vlans and bridge roots subtlv_len:%d", subtlv_len);
+		   XFREE (MTYPE_ISIS_TRILL_VLANBRIDGE_ROOTS, broot);
+		   break;
+		 }
+
+	       if (subtlv_len > 0)
+                 {
+	            broot->bridge_roots_count = subtlv_len / ETHERADDRL;
+	            broot->bridge_roots = XMALLOC (MTYPE_ISIS_TRILL_BRIDGE_ROOTIDS, subtlv_len);
+	            memcpy(broot->bridge_roots, pnt, subtlv_len);
+	            pnt += subtlv_len;
+		 }
+	       subtlvs_len -= subtlv_len;
+	       listnode_add (recvd_nick->broots, broot);
+	       broots_recvd = TRUE;
+	       break;
+
+	     default:
+	       pnt += subtlv_len;
+	       subtlvs_len -= subtlv_len;
+	       break;
+	     }
+         }
+    }
+
+  if (recvd_nick->broots != NULL && broots_recvd == TRUE)
+    {
+      for (ALL_LIST_ELEMENTS_RO(recvd_nick->broots, node, broot))
+        {
+           for (idx=broot->vlan_start; idx <=broot->vlan_end; idx++)
+	      SET_VLAN(recvd_nick->vlans_forwarder, idx);
+        }
+    }
+  return (nick_recvd);
+}
+
+void
+trill_parse_router_capability_tlvs (struct isis_area *area,
+		struct isis_lsp *lsp)
+{
+  nickinfo_t recvd_nick;
+
+  /* Return if LSP is our own or is a pseudonode LSP */
+  if ((memcmp (lsp->lsp_header->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
+       || (LSP_PSEUDO_ID(lsp->lsp_header->lsp_id) != 0))
+    return;
+
+  if (trill_parse_lsp (lsp, &recvd_nick))
+    {
+      /* Parsed LSP correctly but process only if nick is not unknown */
+      if (recvd_nick.nick.name != RBRIDGE_NICKNAME_NONE)
+         trill_nick_recv (area, &recvd_nick);
+    }
+  else 
+    {
+       /* if we have a nickname stored from this RBridge we remove it as this 
+	* LSP without a nickname likely indicates the RBridge has re-started 
+	* and hasn't chosen a new nick.
+        */
+       trill_nick_destroy (lsp);
+    }
+
+  trill_nickinfo_del (&recvd_nick);
+}
+
+/* Lookup nickname when given a system ID */
+u_int16_t
+sysid_to_nick(struct isis_area *area, u_char *sysid)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+
+  dnode = dict_lookup (area->trill->sysidtonickdb, sysid);
+  if (dnode == NULL)
+    return 0;
+  tnode = (nicknode_t *) dnode_get (dnode);
+  return tnode->info.nick.name;
+}
+
+nicknode_t *
+trill_nicknode_lookup(struct isis_area *area, uint16_t nick)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+
+  dnode = dict_lookup (area->trill->nickdb, &nick);
+  if (dnode == NULL) 
+    return (NULL);
+  tnode = (nicknode_t *) dnode_get (dnode);
+  return (tnode);
+}
+
+/* Lookup system ID when given a nickname */
+u_char *
+nick_to_sysid(struct isis_area *area, u_int16_t nick) 
+{
+  nicknode_t *tnode;
+
+  tnode = trill_nicknode_lookup(area, nick);
+  if (tnode == NULL)
+    return (NULL);
+  return tnode->info.sysid;
+}
+
+static void
+trill_destroy_nickfwdtable(void *obj)
+{
+  XFREE (MTYPE_ISIS_TRILL_FWDTBL_NODE, obj);
+}
+
+/*
+ * Creates a nickname forwarding table for TRILL. 
+ * Forwarding table is stored in the per-area fwdtbl list. 
+ */
+static void
+trill_create_nickfwdtable(struct isis_area *area)
+{
+  struct listnode *node;
+  struct isis_vertex *vertex;
+  struct isis_adjacency *adj;
+  struct list *fwdlist = NULL;
+  nickfwdtblnode_t *fwdnode;
+  struct isis_spftree *rdtree;
+  int firstnode = TRUE; 
+
+  rdtree = area->spftree [TRILL_ISIS_LEVEL-1];
+  if (area->trill->fwdtbl)
+    list_delete (area->trill->fwdtbl);
+  area->trill->fwdtbl = NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (rdtree->paths, node, vertex))
+    {
+      if (firstnode)
+        {
+          /* first node in path list is us */
+          fwdlist = list_new();
+	  fwdlist->del = trill_destroy_nickfwdtable;
+	  firstnode = FALSE;
+	  continue;
+        }
+      if (vertex->type != VTYPE_NONPSEUDO_IS &&
+	  vertex->type != VTYPE_NONPSEUDO_TE_IS)
+	continue;
+
+      if (listhead (vertex->Adj_N) && 
+           (adj = listgetdata (listhead (vertex->Adj_N))))
+        {
+          fwdnode = XCALLOC (MTYPE_ISIS_TRILL_FWDTBL_NODE, sizeof(nickfwdtblnode_t));
+          fwdnode->dest_nick = sysid_to_nick (area, vertex->N.id);
+          memcpy(fwdnode->adj_snpa, adj->snpa, sizeof(fwdnode->adj_snpa));
+	  fwdnode->interface = adj->circuit->interface;
+          listnode_add (fwdlist, fwdnode);
+        }
+      else
+        {
+          list_delete (fwdlist);
+	  fwdlist = NULL;
+	  return;
+	}
+    }
+
+  area->trill->fwdtbl = fwdlist;
+}
+
+static void
+trill_fwdtbl_print (struct vty *vty, struct isis_area *area)
+{ 
+  struct listnode *node;
+  nickfwdtblnode_t *fwdnode;
+
+  if (area->trill->fwdtbl == NULL)
+    return;
+
+  vty_out(vty, "RBridge        nickname   interface  nexthop MAC%s", VTY_NEWLINE); 
+  for (ALL_LIST_ELEMENTS_RO (area->trill->fwdtbl, node, fwdnode))
+    {
+      vty_out (vty, "%-15s   %-5d      %-5s  %-15s%s",
+               print_sys_hostname (nick_to_sysid (area, fwdnode->dest_nick)),
+	       ntohs (fwdnode->dest_nick), fwdnode->interface->name,
+	       snpa_print (fwdnode->adj_snpa), VTY_NEWLINE);
+    }
+}
+
+static void
+trill_add_nickadjlist(struct isis_area *area, struct list *adjlist,
+		 struct isis_vertex *vertex)
+{
+  u_int16_t nick;
+
+  nick = sysid_to_nick (area, vertex->N.id);
+  if (!nick)
+    return;
+  if (listnode_lookup (adjlist, (void *)(u_long)nick) != NULL)
+    return;
+  listnode_add (adjlist, (void *)(u_long)nick);
+}
+
+/*
+ * Creates TRILL nickname adjacency lists for each distribution tree (DT).
+ * An adjacency list consists of our (this RBridge) adjacent nodes in the 
+ * campus that are present on the DT paths. It is a subset of our adjacent 
+ * nodes. The adjacency list for a distribution tree is stored inside the 
+ * root dict node of the distribution tree in our nickname database.
+ */
+static void
+trill_create_nickadjlist(struct isis_area *area, nicknode_t *nicknode)
+{
+  struct listnode *node;
+  struct listnode *cnode;
+  struct isis_vertex *vertex;
+  struct isis_vertex *cvertex;
+  struct isis_vertex *rbvertex = NULL;
+  struct list *adjlist;
+  struct list *childlist;
+  struct isis_spftree *rdtree;
+
+  if (nicknode == NULL) 
+   {
+     rdtree = area->spftree[TRILL_ISIS_LEVEL-1];
+     if (area->trill->adjnodes)
+        list_delete (area->trill->adjnodes);
+     area->trill->adjnodes = NULL;
+   }
+  else
+   {
+     rdtree = nicknode->rdtree;
+     if (nicknode->adjnodes)
+       list_delete (nicknode->adjnodes);
+     nicknode->adjnodes = NULL;
+   }
+
+  /* Find our node in the distribution tree first */
+  for (ALL_LIST_ELEMENTS_RO (rdtree->paths, node, vertex))
+    {
+      if (vertex->type != VTYPE_NONPSEUDO_IS &&
+	  vertex->type != VTYPE_NONPSEUDO_TE_IS)
+	continue;
+      if (memcmp (vertex->N.id, area->isis->sysid, ISIS_SYS_ID_LEN) == 0)
+        {
+          rbvertex = vertex;
+	  break;
+	}
+    }
+
+  /* Determine adjacencies by looking up the parent & child nodes */
+  if (rbvertex)
+    {
+      adjlist = list_new();
+
+      if (rbvertex->parent)
+        {
+	  /* 
+	   * Find adjacent parent node: check parent is not another vertex 
+	   * with our system ID and the parent node is on SPF paths 
+	   */
+	  vertex = rbvertex->parent;
+	  while (vertex != NULL)
+	    {
+              if (memcmp (vertex->N.id, area->isis->sysid, ISIS_SYS_ID_LEN)
+		  && (listnode_lookup (rdtree->paths, vertex)))
+		break;
+	      vertex = vertex->parent;
+	    }
+	  if (vertex != NULL)
+            trill_add_nickadjlist (area, adjlist, vertex);
+	}
+
+      if (rbvertex->children && listhead (rbvertex->children)) 
+        {
+	   childlist = list_new();
+           for (ALL_LIST_ELEMENTS_RO (rbvertex->children, node, vertex))
+	     {
+               if (memcmp (vertex->N.id, area->isis->sysid, ISIS_SYS_ID_LEN) == 0)
+                 listnode_add(childlist, vertex);
+	       else if (listnode_lookup (rdtree->paths, vertex))
+                 trill_add_nickadjlist (area, adjlist, vertex);
+	     }
+
+	   /* 
+	    * If we find child vertices above with our system ID then we search
+	    * their descendants and any that are found are added as our adjacencies.
+	    */
+	   for (node = listhead(childlist); node != NULL; node = listnextnode(node))
+             {
+               if ((vertex = listgetdata(node)) == NULL)
+	         break;
+
+               for (ALL_LIST_ELEMENTS_RO (vertex->children, cnode, cvertex))
+	         {
+                   if ((memcmp (cvertex->N.id, area->isis->sysid, ISIS_SYS_ID_LEN) == 0) &&
+                           listnode_lookup(childlist, cvertex) == NULL)
+                     listnode_add(childlist, cvertex);
+
+		   if (listnode_lookup(rdtree->paths, cvertex))
+                     trill_add_nickadjlist (area, adjlist, cvertex);
+	         }
+	     }
+	   list_delete(childlist);
+	}
+
+      if (nicknode != NULL)
+        nicknode->adjnodes = adjlist;
+      else
+	area->trill->adjnodes = adjlist;
+    }
+  trill_create_vlanfilterlist(area, nicknode);
+}
+ 
+static nickfwdtblnode_t *
+trill_fwdtbl_lookup (struct isis_area *area, u_int16_t nick)
+{
+  struct listnode *node;
+  nickfwdtblnode_t *fwdnode;
+
+  if (area->trill->fwdtbl == NULL)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (area->trill->fwdtbl, node, fwdnode))
+    if (fwdnode->dest_nick == nick)
+      return fwdnode;
+
+  return NULL;
+}
+
+static void
+trill_adjtbl_print (struct vty *vty, struct isis_area *area, nicknode_t *nicknode)
+{
+  struct listnode *node;
+  nickfwdtblnode_t *fwdnode;
+  void *listdata;
+  u_int16_t nick;
+  int idx = 0;
+  u_int8_t *vlans;
+  int vlan_set;
+  int vlan;
+  struct list *adjnodes;
+  struct listnode *vnode = NULL;
+  struct list  *vlans_reachable;
+
+  if (nicknode != NULL)
+    {
+      adjnodes = nicknode->adjnodes;
+      vlans_reachable = nicknode->vlans_reachable;
+    }
+  else
+    {
+      adjnodes = area->trill->adjnodes;
+      vlans_reachable = area->trill->vlans_reachable;
+    }
+
+  if (adjnodes == NULL)
+    return;
+
+  if ((vlans_reachable != NULL) &&
+      listcount(adjnodes) == listcount(vlans_reachable))
+    vnode = listhead (vlans_reachable);
+
+  for (ALL_LIST_ELEMENTS_RO (adjnodes, node, listdata))
+    {
+      nick = (u_int16_t)(u_long)listdata;
+      fwdnode = trill_fwdtbl_lookup (area, nick);
+      if (!fwdnode)
+        continue;
+
+      vty_out (vty, "%-15s   %-5d      %-5s  %-15s%s",
+               print_sys_hostname (nick_to_sysid(area, nick)),
+	       ntohs (nick), fwdnode->interface->name,
+	       snpa_print (fwdnode->adj_snpa), VTY_NEWLINE);
+
+      if (vlans_reachable == NULL || vnode == NULL)
+        continue;
+
+      vty_out (vty, "    VLAN filter list:");
+      vlans = listgetdata (vnode);
+      if (vlans == NULL)
+        {
+          vty_out (vty, "%s", VTY_NEWLINE);
+          continue;
+        }
+
+      EACH_VLAN_SET(vlans, vlan, vlan_set)
+       {
+          idx++;
+          if (idx % 8 == 0)
+              vty_out (vty, "%s            ", VTY_NEWLINE);
+          vty_out (vty, "%d ", vlan);
+       }
+      vnode = listnextnode (vnode);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+static void
+trill_adjtbl_print_all (struct vty *vty, struct isis_area *area)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+
+  vty_out(vty, "Adjacencies on our RBridge distribution tree:%s", VTY_NEWLINE);
+  trill_adjtbl_print (vty, area, NULL);
+
+  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
+    {
+      vty_out(vty, "Adjacencies on RBridge %s distribution tree:%s",
+	      print_sys_hostname (tnode->info.sysid), VTY_NEWLINE);
+      trill_adjtbl_print (vty, area, tnode);
+    }
+}
+
+static void
+trill_ioctl(int fd, unsigned type, void *data)
+{
+  if (isisd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
+
+  if (ioctl(fd, type, data) != 0) {
+      zlog_warn ("trill_ioctl() type:%X failed: %s", type, safe_strerror (errno));
+  }
+
+  if (isisd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
+}
+
+static void
+trill_publish_nick(struct isis_area *area, int fd, u_int16_t nick,
+    nickfwdtblnode_t *fwdnode)
+{
+  nicknode_t *nick_node;
+  int adjcount = 0;
+  int dtrootcount = 0;
+  int idx;
+  int size;
+  struct listnode *node;
+  void *listdata;
+  struct list *adjnodes;
+  struct list *dtrootnodes;
+  trill_nickinfo_t *ni;
+  struct list *vlans_reachable;
+
+  /* If this is a forwarding entry (not us), then get node data */
+  if (fwdnode != NULL)
+    {
+      nick_node = trill_nicknode_lookup (area, fwdnode->dest_nick);
+      if (nick_node == NULL)
+	 return;
+      adjnodes = nick_node->adjnodes;
+      dtrootnodes = nick_node->info.dt_roots;
+      vlans_reachable = nick_node->vlans_reachable;
+    }
+  else
+    {
+      adjnodes = area->trill->adjnodes;
+      dtrootnodes = area->trill->dt_roots;
+      vlans_reachable = area->trill->vlans_reachable;
+    }
+
+  if (adjnodes != NULL)
+    adjcount = listcount(adjnodes);
+  if (dtrootnodes != NULL)
+    dtrootcount = listcount(dtrootnodes); 
+
+  size = sizeof(trill_nickinfo_t) + (adjcount * sizeof (u_int16_t)) + 
+	  (dtrootcount * sizeof (u_int16_t)) + 
+	  (adjcount * VLANS_ARRSIZE);
+  ni = (trill_nickinfo_t *)calloc(1, size);
+  ni->tni_adjcount = adjcount;
+  ni->tni_dtrootcount = dtrootcount;
+  ni->tni_nick = nick;
+
+  if (fwdnode != NULL)
+    {
+      memcpy(ni->tni_adjsnpa, fwdnode->adj_snpa,
+	     sizeof(fwdnode->adj_snpa));
+      ni->tni_linkid = fwdnode->interface->ifindex;
+    }
+
+  if (adjcount > 0)
+    {
+      idx = 0;
+      for (ALL_LIST_ELEMENTS_RO (adjnodes, node, listdata))
+        {
+          TNI_ADJNICK(ni, idx) = (u_int16_t)(u_long)listdata;
+	  idx++;
+        }
+    }
+
+  if (dtrootcount > 0)
+    {
+      idx = 0;
+      for (ALL_LIST_ELEMENTS_RO (dtrootnodes, node, listdata))
+        {
+          TNI_DTROOTNICK(ni, idx) = (u_int16_t)(u_long)listdata;
+	  idx++;
+        }
+    }
+
+  if (vlans_reachable != NULL)
+    {
+      idx = 0;
+      for (ALL_LIST_ELEMENTS_RO (vlans_reachable, node, listdata))
+        {
+	  memcpy (TNI_VLANFILTERMAP(ni, idx), listdata, VLANS_ARRSIZE);
+	  idx++;
+        }
+    }
+
+  trill_ioctl (fd, fwdnode == NULL ? TRILL_SETNICK : TRILL_ADDNICK, ni);
+  free(ni);
+}
+
+static void
+trill_publish (struct isis_area *area, struct isis_circuit *trill_circuit)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+  struct listnode *node;
+  nickfwdtblnode_t *fwdnode;
+  u_char *lsysid;
+  u_int16_t lpriority;
+  u_int16_t root_nick;
+
+  trill_ioctl(trill_circuit->fd, TRILL_DELALL, NULL);
+
+  if (area->trill->fwdtbl != NULL)
+    {
+      for (ALL_LIST_ELEMENTS_RO (area->trill->fwdtbl, node, fwdnode))
+        {
+	  trill_publish_nick(area, trill_circuit->fd, fwdnode->dest_nick,
+	    fwdnode);
+        }
+    }
+
+  trill_publish_nick(area, trill_circuit->fd, area->trill->nick.name, NULL);
+
+  /* Compute the highest priority root tree node  */
+  lpriority = area->trill->root_priority;
+  lsysid = area->isis->sysid;
+  root_nick = area->trill->nick.name;
+
+  /*
+   * Highest priority tree root is determined by the numerically lowest
+   * priority field or if priorities are equal then by lowest system ID. 
+   */
+  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
+    {
+      if (tnode->info.root_priority > lpriority)
+        continue;
+      if (tnode->info.root_priority == lpriority &&
+          memcmp(tnode->info.sysid, lsysid, ISIS_SYS_ID_LEN) > 0)
+        continue;
+      lpriority = tnode->info.root_priority;
+      lsysid = tnode->info.sysid;
+      root_nick = tnode->info.nick.name;
+    }
+  trill_ioctl(trill_circuit->fd, TRILL_TREEROOT, &root_nick); 
+}
+
+void
+trill_set_vlan_forwarder (struct isis_circuit *circuit, u_int8_t *forwarder)
+{
+  trill_ioctl(circuit->fd, TRILL_VLANFWDER, forwarder);
+}
+
+void
+trill_port_flush (struct isis_circuit *circuit, u_int16_t vlan)
+{
+  trill_ioctl(circuit->fd, TRILL_PORTFLUSH, (void *)(unsigned long)vlan);
+}
+
+void
+trill_nick_flush (struct isis_circuit *circuit, u_int16_t vlan)
+{
+  trill_ioctl(circuit->fd, TRILL_NICKFLUSH, (void *)(unsigned long)vlan);
+}
+
+/*
+ * Called upon computing the SPF trees to create the forwarding
+ * and adjacency lists for TRILL.
+ */
+void
+trill_process_spf (struct isis_area *area)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+  struct listnode *node;
+  struct isis_circuit *trill_circuit = NULL;
+
+  /* Nothing to do if we don't have a nick yet */
+  if (area->trill->nick.name == RBRIDGE_NICKNAME_NONE)
+	  return;
+
+  if (area->circuit_list && listhead(area->circuit_list))
+    trill_circuit = listgetdata(listhead(area->circuit_list));
+  if (trill_circuit == NULL)
+    return;
+
+  trill_create_nickfwdtable(area);
+  trill_create_nickadjlist(area, NULL);
+  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
+    trill_create_nickadjlist(area, tnode);
+
+  trill_publish(area, trill_circuit);
+
+  for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, trill_circuit))
+    {
+      trill_ioctl(trill_circuit->fd, TRILL_DESIGVLAN,
+	&trill_circuit->vlans->designated);
+      if (trill_circuit->vlans->inhibit_all == 0)
+	trill_set_vlan_forwarder (trill_circuit,
+	    trill_circuit->vlans->forwarder);
+    }
+}
+
+void
+trill_nickdb_print (struct vty *vty, struct isis_area *area)
+{
+  dnode_t *dnode;
+  nicknode_t *tnode;
+  const char *sysid;
+  int vlan_count = 0;
+  int vlan_set;
+  int vlan;
+
+  vty_out(vty, "    System ID          Hostname     Nickname" 
+	 "   Priority%s", VTY_NEWLINE); 
+  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
+    {
+      sysid = sysid_print (tnode->info.sysid);
+      vty_out (vty, "%-21s %-10s  %8d  %8d%s", sysid,
+               print_sys_hostname (tnode->info.sysid),
+	       ntohs (tnode->info.nick.name),
+	       tnode->info.nick.priority, VTY_NEWLINE);
+
+      vty_out (vty, "    VLAN Forwarder: ");
+      EACH_VLAN_SET(tnode->info.vlans_forwarder, vlan, vlan_set)
+        {
+	   vlan_count++;
+	   if (vlan_count % 8 == 0)
+             vty_out(vty, "%s               ", VTY_NEWLINE);
+           vty_out (vty, "%d ", vlan); 
+        }
+      vty_out (vty, "%s", VTY_NEWLINE); 
+    }
+}
+
+static int
+ethercmp(const void *e1, const void *e2)
+{
+  return memcmp (e1, e2, ETH_ALEN);
+}
+
+static int
+gather_bridge_ids(struct isis_area *area,
+    struct trill_vlan_bridge_roots_subtlv *vlantlv, int vlan)
+{
+  time_t now;
+  int circnt;
+  struct listnode *node;
+  struct isis_circuit *circuit;
+  char *bptr, *obptr;
+  ptrdiff_t numbytes;
+
+  now = time (NULL);
+  circnt = 0;
+  bptr = (char *)(vlantlv + 1);
+  for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
+    {
+      if (CHECK_VLAN(circuit->vlans->forwarder, vlan))
+	{
+	  circnt++;
+	  /*
+	   * Note that it's ok for circuits to lack a root bridge ID.  There
+	   * may just not be a bridge out there.  (Ultimately, in the future,
+	   * that's where we'd like to be.)
+	   */
+	  if (circuit->root_expire != 0 && now - circuit->root_expire <= 0)
+	    {
+	      /* ignore bridge priority; only the MAC ID is wanted */
+	      memcpy (bptr, circuit->root_bridge + 2, ETH_ALEN);
+	      bptr += ETH_ALEN;
+	    }
+	}
+    }
+
+  /* Sort the bridge IDs for ease of comparison, and then remove dups */
+  obptr = (char *)(vlantlv + 1);
+  numbytes = bptr - obptr;
+  if (numbytes > ETH_ALEN)
+    {
+      qsort(obptr, numbytes / ETH_ALEN, ETH_ALEN, ethercmp);
+      while (obptr < bptr - ETH_ALEN)
+	{
+	  if (memcmp (obptr, obptr + ETH_ALEN, ETH_ALEN) == 0)
+	    {
+	      memmove (obptr, obptr + ETH_ALEN, (bptr - obptr) - ETH_ALEN);
+	      bptr -= ETH_ALEN;
+	      numbytes -= ETH_ALEN;
+	    }
+	  else
+	    {
+	      obptr += ETH_ALEN;
+	    }
+	}
+    }
+
+  /* Store the root bridge byte count here for the caller */
+  vlantlv->vlan_end = numbytes;
+  return circnt;
+}
+
+/* 
+ * Add TLVs necessary to advertise TRILL nickname using router capabilities TLV
+ */
+int
+tlv_add_trill_nickname(struct trill_nickname *nick_info,
+  struct stream *stream, struct  isis_area *area)
+{
+  size_t tlvstart;
+  struct router_capability_tlv rtcap;
+  u_char tflags;
+  struct trill_nickname_subtlv tn;
+  int rc;
+  int vlan;
+  int circnt;
+  int nbytes;
+  struct trill_vlan_bridge_roots_subtlv *lastvlantlv, *nextvlantlv;
+
+  tlvstart = stream_get_endp (stream);
+
+  (void) memset(&rtcap, 0, sizeof (rtcap));
+  rc = add_tlv(ROUTER_CAPABILITY, sizeof (rtcap), (uchar_t *)&rtcap, stream);
+  if (rc != ISIS_OK)
+    return rc;
+
+  tflags = TRILL_FLAGS_V0;
+  rc = add_subtlv (RCSTLV_TRILL_FLAGS, sizeof (tflags), (uchar_t *)&tflags,
+      tlvstart, stream);
+  if (rc != ISIS_OK)
+    return rc;
+
+  tn.tn_priority = nick_info->priority;
+  tn.tn_nickname = nick_info->name;
+  tn.tn_trootpri = htons(area->trill->root_priority);
+  tn.tn_treecount = htons(0);
+  rc = add_subtlv (RCSTLV_TRILL_NICKNAME, sizeof (tn), (uchar_t *)&tn, tlvstart,
+      stream);
+  if (rc != ISIS_OK)
+    return rc;
+
+  /*
+   * The algorithm below is designed to find the set of VLANs for which we are
+   * appointed forwarder for at least one circuit, and organize them into lists
+   * (each with a separate sub-TLV) based on root 802.1D bridge ID.  The lists
+   * must be contiguous and must have exactly the same set of root IDs, but
+   * need not have the same set of circuits involved.
+   *
+   * We currently don't support multicast snooping, so the complexities of the
+   * M4/M6/OM bits are spared here.
+   */
+  circnt = listcount(area->circuit_list);
+  if (circnt == 0)
+    return rc;
+
+  /* Aligned: Ethernet addresses are 6 bytes, and the subTLV uses uint16_t */
+  lastvlantlv = XMALLOC (MTYPE_ISIS_TRILL_VLANSUBTLV,
+      2 * (sizeof (*lastvlantlv) + circnt * ETH_ALEN));
+  nextvlantlv = (struct trill_vlan_bridge_roots_subtlv *)
+      ((char *)(lastvlantlv + 1) + circnt * ETH_ALEN);
+
+  vlan = circnt = 0;
+  while (vlan <= VLAN_MAX)
+    {
+      /*
+       * If this is the first VLAN or if the last pass ended on an unused VLAN,
+       * then scan ahead to find the start of the next range that's in use.
+       * Otherwise, copy down the last one found.
+       */
+      if (circnt == 0)
+	{
+	  for (vlan++; vlan <= VLAN_MAX; vlan++)
+	  {
+	    circnt = gather_bridge_ids (area, lastvlantlv, vlan);
+	    if (circnt != 0)
+	      break;
+	  }
+	  if (circnt == 0)
+	    break;
+	}
+      else
+	{
+	  memcpy (lastvlantlv, nextvlantlv,
+	      nextvlantlv->vlan_end + sizeof (*nextvlantlv));
+	}
+
+      /*
+       * Set the multicast bits, because we don't support IGMP/MLD
+       * snooping, and we thus need to see all multicast frames.
+       */
+      lastvlantlv->vlan_start = htons (vlan | TVRFS_M4 |  TVRFS_M6 |  TVRFS_OM);
+      nbytes = lastvlantlv->vlan_end;
+
+      /*
+       * Now locate the end of the compatible set of VLANs: these are the ones
+       * where we're appointed forwarder on at least one circuit, and the list
+       * of root bridge IDs is identical to the current one.
+       */
+      for (vlan++; vlan <= VLAN_MAX; vlan++)
+	{
+	  circnt = gather_bridge_ids (area, nextvlantlv, vlan);
+	  if (circnt == 0 || nbytes != nextvlantlv->vlan_end ||
+	      memcmp (lastvlantlv + 1, nextvlantlv + 1, nbytes != 0))
+	    break;
+	}
+
+      lastvlantlv->vlan_end = htons (vlan - 1);
+
+      /*
+       * Insert the subTLV into the list, starting a new TLV if it won't fit in
+       * the current one.
+       */
+      nbytes += sizeof (*lastvlantlv);
+      rc = add_subtlv (RCSTLV_TRILL_VLANSROOTS, nbytes, (uchar_t *)lastvlantlv,
+	  tlvstart, stream);
+      if (rc == ISIS_WARNING)
+      {
+	tlvstart = stream_get_endp (stream);
+	rc = add_tlv(ROUTER_CAPABILITY, sizeof (rtcap), (uchar_t *)&rtcap,
+	    stream);
+	if (rc != ISIS_OK)
+	  break;
+	rc = add_subtlv (RCSTLV_TRILL_VLANSROOTS, nbytes,
+	    (uchar_t *)lastvlantlv, tlvstart, stream);
+	if (rc != ISIS_OK)
+	  break;
+      }
+    }
+
+  XFREE (MTYPE_ISIS_TRILL_VLANSUBTLV, lastvlantlv);
+
+  return rc;
+}
+
+DEFUN (debug_trill_events,
+       debug_trill_events_cmd,
+       "debug trill events",
+       DEBUG_STR
+       "IS-IS information\n"
+       "IS-IS TRILL Events\n")
+{
+  isis->debugs |= DEBUG_TRILL_EVENTS;
+  print_debug (vty, DEBUG_TRILL_EVENTS, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_trill_events,
+       no_debug_trill_events_cmd,
+       "no debug trill events",
+       UNDEBUG_STR
+       "IS-IS information\n"
+       "IS-IS TRILL Events\n")
+{
+  isis->debugs &= ~DEBUG_TRILL_EVENTS;
+  print_debug (vty, DEBUG_TRILL_EVENTS, 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_trill_nickdatabase,
+       show_trill_nickdatabase_cmd,
+       "show trill nickname database",
+       SHOW_STR TRILL_STR "TRILL IS-IS nickname information\n"
+       "IS-IS TRILL nickname database\n")
+{
+  struct listnode *node;
+  struct isis_area *area;
+
+  if (!isis->trill_active || (isis->area_list->count == 0))
+    return CMD_SUCCESS;
+
+  assert (isis->area_list->count == 1);
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      vty_out (vty, "Area %s nickname:%d priority:%d%s%s",
+          area->area_tag ? area->area_tag : "null", 
+          ntohs(area->trill->nick.name), area->trill->nick.priority,
+          VTY_NEWLINE, VTY_NEWLINE);
+      vty_out (vty, "IS-IS TRILL nickname database:%s", VTY_NEWLINE);
+          trill_nickdb_print (vty, area);
+    }
+
+  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_trill_fwdtable,
+       show_trill_fwdtable_cmd,
+       "show trill forwarding",
+       SHOW_STR TRILL_STR
+       "IS-IS TRILL forwarding table\n")
+{
+  struct listnode *node;
+  struct isis_area *area;
+
+  if (!isis->trill_active || (isis->area_list->count == 0))
+    return CMD_SUCCESS;
+
+  assert (isis->area_list->count == 1);
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      vty_out (vty, "IS-IS TRILL forwarding table:%s", VTY_NEWLINE);
+      trill_fwdtbl_print (vty, area);
+    }
+
+  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_trill_circuits,
+       show_trill_circuits_cmd,
+       "show trill circuits",
+       SHOW_STR TRILL_STR
+       "IS-IS TRILL circuits\n")
+{
+  struct listnode *node;
+  struct isis_area *area;
+
+  if (!isis->trill_active || (isis->area_list->count == 0))
+    return CMD_SUCCESS;
+
+  assert (isis->area_list->count == 1);
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      vty_out (vty, "IS-IS TRILL circuits:%s%s",
+		      VTY_NEWLINE, VTY_NEWLINE);
+      trill_circuits_print_all (vty, area);
+    }
+
+  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_trill_adjtable,
+       show_trill_adjtable_cmd,
+       "show trill adjacencies",
+       SHOW_STR TRILL_STR
+       "IS-IS TRILL adjacency lists\n")
+{
+  struct listnode *node;
+  struct isis_area *area;
+
+  if (!isis->trill_active || (isis->area_list->count == 0))
+    return CMD_SUCCESS;
+
+  assert (isis->area_list->count == 1);
+
+  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+    {
+      vty_out (vty, "IS-IS TRILL adjacencies in all distribution trees:%s%s",
+		      VTY_NEWLINE, VTY_NEWLINE);
+          trill_adjtbl_print_all (vty, area);
+    }
+
+  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+/*
+ * Enable TRILL support in IS-IS command, only one IS-IS area allowed.
+ */
+DEFUN (isis_trill,
+       isis_trill_cmd,
+       "isis trill",
+       "Enable use of IS-IS as routing protocol for TRILL\n")
+{
+  if (!isis->trill_active && isis->area_list->count > 0)
+    {
+      vty_out (vty, "Cannot enable TRILL. IS-IS area already configured%s",
+		      VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  isis->trill_active = TRUE;
+  return CMD_SUCCESS;
+}
+
+/*
+ * Disable TRILL support in IS-IS command
+ */
+DEFUN (no_isis_trill,
+       no_isis_trill_cmd,
+       "no isis trill",
+       "Disable use of IS-IS as routing protocol for TRILL\n")
+{
+  isis->trill_active = FALSE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (trill_nickname,
+       trill_nickname_cmd,
+       "trill nickname WORD",
+       TRILL_STR
+       TRILL_NICK_STR
+       "<1-65534>\n")
+{
+  struct isis_area *area;
+  u_int16_t nickname;
+
+  area = vty->index;
+  assert (area);
+  assert (area->isis);
+  if (!area->isis->trill_active)
+    {
+      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER_RANGE ("TRILL nickname", nickname, argv[0], 
+		  RBRIDGE_NICKNAME_MIN + 1, RBRIDGE_NICKNAME_MAX);
+  if (!trill_area_nickname (area, nickname))
+    {
+      vty_out (vty, "TRILL nickname conflicts with another RBridge nickname,"
+		    " must select another.%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_trill_nickname,
+       no_trill_nickname_cmd,
+       "no trill nickname",
+       TRILL_STR
+       TRILL_NICK_STR)
+{
+  struct isis_area *area;
+
+  area = vty->index;
+  assert (area);
+  assert (area->isis);
+  if (!area->isis->trill_active)
+    {
+      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  trill_area_nickname (area, 0);
+  return CMD_SUCCESS;
+}
+
+DEFUN (trill_nickname_priority,
+       trill_nickname_priority_cmd,
+       "trill nickname priority WORD",
+       TRILL_STR
+       TRILL_NICK_STR
+       "priority of use field\n"
+       "<1-127>\n")
+{
+  struct isis_area *area;
+  u_int8_t priority;
+
+  area = vty->index;
+  assert (area);
+  assert (area->isis);
+  if (!area->isis->trill_active)
+    {
+      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER_RANGE ("TRILL nickname priority", priority, argv[0],
+		  MIN_RBRIDGE_PRIORITY, MAX_RBRIDGE_PRIORITY);
+  trill_nickname_priority_update (area, priority);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_trill_nickname_priority,
+       no_trill_nickname_priority_cmd,
+       "no trill nickname priority WORD",
+       TRILL_STR
+       TRILL_NICK_STR
+       "priority of use field\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+  assert (area);
+  assert (area->isis);
+  if (!area->isis->trill_active)
+    {
+      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  trill_nickname_priority_update (area, 0);
+  return CMD_SUCCESS;
+}
+
+DEFUN (trill_instance,
+       trill_instance_cmd,
+       "trill instance WORD",
+       TRILL_STR
+       "TRILL instance\n"
+       "instance name\n")
+{
+  struct isis_area *area;
+
+  area = vty->index;
+  assert (area);
+  assert (area->isis);
+  if (!area->isis->trill_active)
+    {
+      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  (void) strlcpy(area->trill->name, argv[0], MAXLINKNAMELEN);
+  return CMD_SUCCESS;
+}
+
+void
+install_trill_elements (void)
+{
+  install_element (VIEW_NODE, &show_trill_nickdatabase_cmd);
+  install_element (VIEW_NODE, &show_trill_fwdtable_cmd);
+  install_element (VIEW_NODE, &show_trill_adjtable_cmd);
+  install_element (VIEW_NODE, &show_trill_circuits_cmd);
+
+  install_element (ENABLE_NODE, &debug_trill_events_cmd);
+  install_element (ENABLE_NODE, &no_debug_trill_events_cmd);
+  install_element (ENABLE_NODE, &show_trill_nickdatabase_cmd);
+  install_element (ENABLE_NODE, &show_trill_fwdtable_cmd);
+  install_element (ENABLE_NODE, &show_trill_adjtable_cmd);
+  install_element (ENABLE_NODE, &show_trill_circuits_cmd);
+
+  install_element (CONFIG_NODE, &debug_trill_events_cmd);
+  install_element (CONFIG_NODE, &no_debug_trill_events_cmd);
+  install_element (CONFIG_NODE, &isis_trill_cmd);
+  install_element (CONFIG_NODE, &no_isis_trill_cmd);
+
+  install_element (ISIS_NODE, &trill_nickname_cmd);
+  install_element (ISIS_NODE, &no_trill_nickname_cmd);
+  install_element (ISIS_NODE, &trill_nickname_priority_cmd);
+  install_element (ISIS_NODE, &no_trill_nickname_priority_cmd);
+  install_element (ISIS_NODE, &trill_instance_cmd);
+
+  install_trill_vlan_elements ();
+}
+
+static int
+update_link(dladm_handle_t handle, datalink_id_t linkid, void *arg)
+{
+  struct isis_area *area = arg;
+  dladm_status_t status;
+  char bridge[MAXLINKNAMELEN], linkname[MAXLINKNAMELEN];
+  char pointless[DLADM_STRSIZE];
+  datalink_class_t class;
+  struct interface *ifp;
+  struct isis_circuit *circ;
+  uint_t propval, valcnt;
+
+  status = dladm_bridge_getlink (handle, linkid, bridge, sizeof (bridge));
+  if (status != DLADM_STATUS_OK || strcmp (bridge, area->trill->name) != 0)
+    return DLADM_WALK_CONTINUE;
+
+  status = dladm_datalink_id2info (handle, linkid, NULL, &class, NULL,
+      linkname, sizeof (linkname));
+  if (status == DLADM_STATUS_OK)
+    {
+      ifp = if_get_by_name (linkname);
+      ifp->ifindex = linkid;
+      ifp->flags |= IFF_UP | IFF_RUNNING;
+
+      /*
+       * This value is arbitrary.  The real interface MTU will be read out of
+       * the kernel when isis_circuit_up calls the TRILL socket interface.
+       */
+      if (ifp->mtu == 0)
+	ifp->mtu = 1500;
+      *(datalink_id_t *)ifp->sdl.sdl_data = linkid;
+      ifp->sdl.sdl_nlen = sizeof (datalink_id_t);
+      if ((circ = ifp->info) == NULL)
+	{
+	  circ = isis_csm_state_change (IF_UP_FROM_Z, NULL, ifp);
+	  circ = isis_csm_state_change (ISIS_ENABLE, circ, area);
+	}
+      /*
+       * The second state change has caused us to open up the socket for this
+       * link and read the Ethernet address.  Copy that into place for the
+       * interface structure.
+       */
+      ifp->sdl.sdl_alen = ETH_ALEN;
+      memcpy (LLADDR (&ifp->sdl), circ->u.bc.snpa, ETH_ALEN);
+      valcnt = 1;
+      status = dladm_get_linkprop_values (dlhandle, linkid,
+	  DLADM_PROP_VAL_PERSISTENT, "default_tag", &propval, &valcnt);
+      if (status == DLADM_STATUS_OK)
+	circ->vlans->pvid = propval;
+      memset (circ->vlans->enabled, 0, VLANS_ARRSIZE);
+      if (circ->vlans->pvid != 0)
+	SET_VLAN (circ->vlans->enabled, circ->vlans->pvid);
+    }
+  else
+    {
+      zlog_err ("unable to get link info for ID %u: %s", linkid,
+	  dladm_status2str (status, pointless));
+    }
+  return DLADM_WALK_CONTINUE;
+}
+
+static int
+set_vlan(dladm_handle_t handle, datalink_id_t linkid, void *arg)
+{
+  dladm_status_t status;
+  dladm_vlan_attr_t vinfo;
+  char pointless[DLADM_STRSIZE];
+  struct interface *ifp;
+  struct isis_circuit *circuit;
+
+  status = dladm_vlan_info (handle, linkid, &vinfo, DLADM_OPT_ACTIVE);
+  if (status != DLADM_STATUS_OK)
+  {
+    zlog_debug ("can't get VLAN info on link ID %u: %s",
+	linkid, dladm_status2str (status, pointless));
+    return DLADM_WALK_CONTINUE;
+  }
+
+  ifp = if_lookup_by_index (vinfo.dv_linkid);
+  if (ifp != NULL)
+  {
+    circuit = ifp->info;
+    SET_VLAN (circuit->vlans->enabled, vinfo.dv_vid);
+  }
+  return DLADM_WALK_CONTINUE;
+}
+
+static char
+trill_internal_reload(struct isis_area *area)
+{
+  struct interface *ifp;
+  struct listnode *node, *nnode;
+  struct isis_circuit *circ;
+  dladm_status_t status;
+  char errmsg[DLADM_STRSIZE];
+
+  if ((status = dladm_open (&dlhandle)) != DLADM_STATUS_OK)
+    {
+      zlog_err ("%s: unable to open datalink control: %s",
+          area->trill->name, dladm_status2str(status, errmsg));
+      return FALSE;
+    }
+
+  /*
+   * Turn off the IFF_UP bit for every link.  Any links left over at the end
+   * without that flag must have been removed.
+   */
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    ifp->flags &= ~IFF_UP;
+
+  /* Get all of the links configured on this bridge */
+  dladm_walk_datalink_id (update_link, dlhandle, area, DATALINK_CLASS_ALL,
+      DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+
+  /* Disable ones that have been removed */
+  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
+    {
+      if (!(ifp->flags & IFF_UP) && (circ = ifp->info) != NULL)
+	{
+	  isis_csm_state_change (ISIS_DISABLE, circ, area);
+	  isis_csm_state_change (IF_DOWN_FROM_Z, circ, area);
+	}
+    }
+
+  /* Now get the VLANs */
+  dladm_walk_datalink_id (set_vlan, dlhandle, area, DATALINK_CLASS_VLAN,
+      DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+
+  dladm_close (dlhandle);
+  return TRUE;
+}
+
+/*
+ * This is run synchronously by the interrupt handling logic when SIGHUP
+ * occurs.  We use this to signal a "refresh" from SMF.  If the user has
+ * specified an explicit configuration file, or if the update fails, then we
+ * just fall through to the normal reload (by way of exec) mechanism.
+ */
+char
+trill_reload(void)
+{
+  if (cfile_present)
+    return FALSE;
+  else
+    return trill_internal_reload (listgetdata (listhead (isis->area_list)));
+}
+
+/*
+ * This function runs before the regular configuration file (if any) is read,
+ * and simulates a configuration read by setting up internal information based
+ * on data stored in dladm.  The user may override this configuration (for
+ * debugging purposes) by specifying a configuration file on the command line.
+ * Otherwise, we force the caller to read /dev/null.
+ */
+void
+trill_read_config (char **cfilep, int argc, char **argv)
+{
+  const char *instname;
+  u_int16_t nickname;
+  struct isis_area *area;
+  struct listnode *ifnode;
+  struct interface *ifp;
+  struct area_addr *addr;
+
+  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, LOG_WARNING);
+
+  if (optind != argc - 1)
+  {
+    zlog_err ("instance name is required for TRILL");
+    exit (1);
+  }
+  instname = argv[optind];
+
+  isis->trill_active = TRUE;
+  area = isis_area_create (instname);
+  (void) strlcpy (area->trill->name, instname, MAXLINKNAMELEN);
+
+  /* Set up to use new (extended) metrics only. */
+  area->newmetric = 1;
+  area->oldmetric = 0;
+
+  /* IS-IS for TRILL is different from the standard; it uses one area address */
+  isis->max_area_addrs = 1;
+
+  if (!trill_internal_reload (area))
+    exit(1);
+
+  /* Recover a previous nickname, if any. */
+  nickname = dladm_bridge_get_nick(instname);
+  if (nickname != RBRIDGE_NICKNAME_NONE && is_nickname_used (nickname))
+  {
+    zlog_warn ("%s: unable to use previous nickname %u", instname, nickname);
+    nickname = RBRIDGE_NICKNAME_NONE;
+  }
+  if (nickname != RBRIDGE_NICKNAME_NONE)
+  {
+    area->trill->nick.name = htons (nickname);
+    SET_FLAG (area->trill->status, TRILL_NICK_SET);
+    SET_FLAG (area->trill->status, TRILL_AUTONICK);
+  }
+
+  /* Set up the area and system ID */
+  ifnode = listhead (iflist);
+  if (ifnode != NULL)
+    {
+      ifp = listgetdata (ifnode);
+      addr = XMALLOC (MTYPE_ISIS_AREA_ADDR, sizeof (struct area_addr));
+      addr->addr_len = 8;
+      addr->area_addr[0] = 0;
+      memcpy (addr->area_addr + 1, LLADDR (&ifp->sdl), ifp->sdl.sdl_alen);
+      addr->area_addr[7] = 0;
+      memcpy (isis->sysid, GETSYSID (addr, ISIS_SYS_ID_LEN), ISIS_SYS_ID_LEN);
+      isis->sysid_set = 1;
+      /* Forget the systemID part of the address */
+      addr->addr_len -= (ISIS_SYS_ID_LEN + 1);
+      listnode_add (area->area_addrs, addr);
+      lsp_l1_generate (area);
+      lsp_l2_generate (area);
+    }
+
+  if (*cfilep == NULL)
+    {
+      *(const char **)cfilep = "/dev/null";
+      cfile_present = FALSE;
+    }
+}
diff --git isisd/isis_trill.h isisd/isis_trill.h
new file mode 100644
index 0000000..88b9f83
--- /dev/null
+++ isisd/isis_trill.h
@@ -0,0 +1,148 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_trill.h
+ *
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ *
+ * 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 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_ISIS_TRILL_H
+#define _ZEBRA_ISIS_TRILL_H
+
+#define	ISO_BPDU	0x42
+
+/* IETF TRILL protocol defined constants */
+#define DFLT_NICK_PRIORITY 0x40			/* Default priority for autogen nicks */
+#define CONFIGURED_NICK_PRIORITY 0x80		/* MSB of priority set if nick is configured */
+#define MIN_RBRIDGE_PRIORITY 1			/* Min priority of use value */
+#define MAX_RBRIDGE_PRIORITY 127		/* Max priority of use value */
+#define MAX_RBRIDGE_NODES (RBRIDGE_NICKNAME_MAX + 1) /* Max RBridges possible */
+#define TRILL_NICKNAME_LEN   2			/* 16-bit nickname */
+#define TRILL_DFLT_ROOT_PRIORITY 0x8000		/* Default tree root priority */
+
+/* Constants used in nickname generation/allocation */
+#define NICKNAMES_BITARRAY_SIZE (MAX_RBRIDGE_NODES / 8) /* nick usage array */
+#define CLEAR_BITARRAY_ENTRYLEN 4	   /* stores nicks available per 32 nicks in nick bitarray */
+#define CLEAR_BITARRAY_ENTRYLENBITS (4*8)  /* 32 nicks tracked in each entry */
+#define CLEAR_BITARRAY_SIZE (MAX_RBRIDGE_NODES / CLEAR_BITARRAY_ENTRYLENBITS)
+
+/* Constants used to track LSP DB acquisition */
+#define MIN_LSPDB_ACQTRIES 2	/* min two LSP PSNP/CSNP send/recv for LSP DB acquisition */
+#define MAX_LSPDB_ACQTRIES 6	/* max LSP PSNP/CSNP send/recv for LSP DB acquisition on any circuit */
+
+/* Macros used to track LSP DB acquisition */
+#define LSPDB_ACQTRYINC(F, C) ((F)->trill->lspdb_acq_reqs[(C)])++
+#define LSPDB_ACQTRYVAL(F, C) ((F)->trill->lspdb_acq_reqs[(C)])
+
+/* trill_info status flags */
+#define TRILL_AUTONICK       (1 << 0)  /* nickname auto-generated (else user-provided) */
+#define TRILL_LSPDB_ACQUIRED (1 << 1)  /* LSP DB acquired before autogen nick is advertised */
+#define TRILL_NICK_SET       (1 << 2)  /* nickname configured (random/user generated) */
+#define TRILL_PRIORITY_SET   (1 << 3)  /* nickname priority configured by user */
+
+/* TRILL information (area-specific) */
+struct trill_info
+{
+  struct trill_nickname nick;   /* our nick */
+  int status; 			/* status flags */
+  dict_t *nickdb;	  	/* TRILL nickname database */
+  dict_t *sysidtonickdb;  	/* TRILL sysid-to-nickname database */
+  /* counter used in LSP database acquisition (per circuit) */
+  u_int8_t lspdb_acq_reqs [ISIS_MAX_CIRCUITS_COUNT];
+  struct list *fwdtbl;          /* RBridge forwarding table */
+  struct list *adjnodes;	/* Adjacent nicks for our distrib. tree */
+  struct list *dt_roots;	/* Our choice of DT roots */
+  struct list *vlans_reachable; /* Per adj and per tree vlans reachable downstream list */
+  u_int16_t root_priority;      /* Root tree priority */
+  char name[MAXLINKNAMELEN];	/* instance name */
+};
+
+/* TRILL nickname information (node-specific) */
+typedef struct nickinfo
+{
+  struct trill_nickname nick;       /* Nick of the node  */
+  u_char sysid[ISIS_SYS_ID_LEN];    /* NET/sysid of node */
+  u_int8_t flags;                   /* TRILL flags advertised by node */
+  struct list *dt_roots;            /* Distrib. Trees chosen by node */
+  u_int16_t root_priority;          /* Root tree priority */
+  u_int16_t root_count;		    /* Root tree count */
+  struct list *broots;		    /* VLANs and Bridge roots */
+  u_int8_t vlans_forwarder[VLANS_ARRSIZE];
+} nickinfo_t;
+
+/* Nickname database node */
+typedef struct trill_nickdb_node
+{
+  nickinfo_t info;		/* Nick info of the node */
+  struct isis_spftree *rdtree;  /* RBridge distribution tree with this nick as root */
+  struct list *adjnodes;	/* Our (host RBridge) adjacent nicks on this distrib. tree */
+  struct list *vlans_reachable; /* Per adj and per tree vlans reachable downstream list */
+  u_int32_t refcnt;		/* ref count */
+} nicknode_t;
+
+/* RBridge search function return status codes */
+typedef enum
+{
+  NOTFOUND = 1,
+  FOUND,
+  DUPLICATE,
+  NICK_CHANGED,
+  PRIORITY_CHANGE_ONLY
+} nickdb_search_result;
+
+/* LSP database acquisition process states */
+typedef enum
+{
+  CSNPRCV = 0,
+  CSNPSND,
+  PSNPSNDTRY,
+} lspdbacq_state;
+
+/* RBridge forwarding table node (1 table per area) */
+typedef struct nickfwdtable_node
+{
+  u_int16_t dest_nick;               /* destination RBridge nick */
+  u_char adj_snpa[ETH_ALEN];         /* MAC address of the adj node */
+  struct interface *interface;       /* if to reach the adj/neigh */
+} nickfwdtblnode_t;
+
+void trill_read_config (char **, int, char **);
+void trill_area_init(struct isis_area *);
+void trill_area_free(struct isis_area *);
+void trill_get_area_nickinfo(struct isis_area *, struct trill_nickname *);
+void trill_nickdb_print (struct vty *, struct isis_area *);
+void trill_nick_destroy(struct isis_lsp *);
+void trill_lspdb_acquire_event(struct isis_circuit *, lspdbacq_state);
+int trill_area_nickname(struct isis_area *, u_int16_t);
+void trill_parse_router_capability_tlvs (struct isis_area *, struct isis_lsp *);
+void trill_process_spf (struct isis_area *);
+void trill_process_hello(struct isis_adjacency *, struct list *);
+void send_trill_vlan_hellos(struct isis_circuit *);
+void trill_circuits_print_all (struct vty *, struct isis_area *);
+u_char *nick_to_sysid(struct isis_area *, u_int16_t); 
+u_int16_t sysid_to_nick(struct isis_area *, u_char *);
+void trill_create_vlanfilterlist(struct isis_area *, nicknode_t *);
+nicknode_t * trill_nicknode_lookup(struct isis_area *, uint16_t);
+void install_trill_elements (void);
+void install_trill_vlan_elements (void);
+int trill_process_bpdu (struct isis_circuit *, u_char *);
+int trill_send_bpdu (struct isis_circuit *circuit, const void *, size_t);
+void trill_send_tc_bpdus (struct isis_circuit *);
+void trill_set_vlan_forwarder (struct isis_circuit *, u_int8_t *);
+void trill_port_flush (struct isis_circuit *, u_int16_t);
+void trill_nick_flush (struct isis_circuit *, u_int16_t);
+void trill_inhib_all(struct isis_circuit *);
+char trill_reload(void);
+#endif
diff --git isisd/isis_trillbpdu.c isisd/isis_trillbpdu.c
new file mode 100644
index 0000000..2352cfc
--- /dev/null
+++ isisd/isis_trillbpdu.c
@@ -0,0 +1,202 @@
+/*
+ * TRILL BPDU handling - isis_trillbpdu.c
+ *
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ *
+ * 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 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <zebra.h>
+#include <time.h>
+#include "log.h"
+#include "if.h"
+#include "stream.h"
+#include "vty.h"
+#include "dict.h"
+#include "isis_common.h"
+#include "isis_constants.h"
+#include "isis_circuit.h"
+#include "isis_tlv.h"
+#include "isis_flags.h"
+#include "isis_vlans.h"
+#include "isis_lsp.h"
+#include "isis_trill.h"
+#include "isisd.h"
+
+/*
+ * This module supports just the bare minimum of Bridge PDU handling necessary
+ * for normal TRILL interaction with standard bridges.  It does not include
+ * spanning tree or other BPDU functions.
+ */
+
+struct common_bpdu
+{
+  u_int16_t cmb_protid;		/* Protocol Identifier */
+  u_int8_t cmb_protvers;	/* Protocol Version Identifier */
+  u_int8_t cmb_type;		/* BPDU Type */
+};
+
+#ifdef __SUNPRO_C
+#pragma pack(1)
+#endif
+
+struct conf_bpdu
+{
+  struct common_bpdu cb_cmb;
+  u_int8_t cb_flags;		/* BPDU Flags */
+  u_int8_t cb_rootid[8];	/* Root Identifier */
+  u_int8_t cb_unused[14];	/* Root Path Cost, Bridge ID, Port ID */
+  u_int16_t cb_messageage;	/* Message Age */
+  u_int16_t cb_maxage;		/* Max Age */
+  u_int16_t cb_hello;		/* Hello Time */
+  u_int16_t cb_unused2;		/* Forward Delay */
+} __attribute__ ((packed));
+
+#ifdef __SUNPRO_C
+#pragma pack()
+#endif
+
+#define	BPDU_PROTID		0	/* Standard STP and RSTP */
+#define	BPDU_PROTVERS_STP	0	/* STP */
+#define	BPDU_PROTVERS_RSTP	2	/* RSTP */
+#define	BPDU_FLAGS_TC_ACK	0x80
+#define	BPDU_FLAGS_TC		1
+#define	BPDU_TYPE_CONF		0
+#define	BPDU_TYPE_RCONF		2
+#define	BPDU_TYPE_TCNOTIF	0x80
+
+int
+trill_process_bpdu (struct isis_circuit *circuit, u_char *srcaddr)
+{
+  size_t bpdulen;
+  struct conf_bpdu cb;
+  time_t now;
+  int brcmp;
+
+  /*
+   * Standard BPDU validation first.  Unrecognized things are just returned
+   * silently.  Bad things (protocol violations) generate warnings.
+   */
+  bpdulen = stream_get_endp (circuit->rcv_stream);
+  if (bpdulen < sizeof (cb.cb_cmb))
+    return ISIS_WARNING;
+
+  stream_get (&cb.cb_cmb, circuit->rcv_stream, sizeof (cb.cb_cmb));
+  if (ntohs(cb.cb_cmb.cmb_protid) != BPDU_PROTID)
+    return ISIS_OK;
+
+  switch (cb.cb_cmb.cmb_type)
+  {
+    case BPDU_TYPE_CONF:
+      if (bpdulen < sizeof (cb))
+	return ISIS_WARNING;
+      stream_get (&cb.cb_cmb + 1, circuit->rcv_stream,
+	  sizeof (cb) - sizeof (cb.cb_cmb));
+      if (ntohs(cb.cb_messageage) >= ntohs(cb.cb_maxage))
+	return ISIS_WARNING;
+      /*
+       * We don't send Configuration BPDUs, so no need to check Bridge & Port
+       * ID values.
+       */
+      break;
+    case BPDU_TYPE_RCONF:
+      if (bpdulen < sizeof (cb) + 1)
+	return ISIS_WARNING;
+      stream_get (&cb.cb_cmb + 1, circuit->rcv_stream,
+	  sizeof (cb) - sizeof (cb.cb_cmb));
+      break;
+    case BPDU_TYPE_TCNOTIF:
+      return ISIS_OK;
+    default:
+      return ISIS_WARNING;
+  }
+
+  brcmp = memcmp (cb.cb_rootid, circuit->root_bridge, sizeof (cb.cb_rootid));
+  now = time (NULL);
+  if (circuit->root_expire == 0 || now - circuit->root_expire > 0 || brcmp <= 0)
+    {
+      int hellot;
+
+      hellot = ntohs(cb.cb_hello) / 256;
+      if (hellot < 1)
+	hellot = 1;
+      else if (hellot > 10)
+	hellot = 10;
+      circuit->root_expire = now + 3 * hellot;
+      memcpy(circuit->root_bridge, cb.cb_rootid, sizeof (cb.cb_rootid));
+
+      /* If root bridge change, then inhibit for a while */
+      if (brcmp != 0)
+	trill_inhib_all (circuit);
+
+     /*
+      * If we've gotten a Topology Change Ack from the root bridge, then we
+      * need not send any more TC notifications.
+      */
+      if ((cb.cb_flags & BPDU_FLAGS_TC) && circuit->tc_count != 0)
+	{
+	  thread_cancel (circuit->tc_thread);
+	  circuit->tc_thread = NULL;
+	  circuit->tc_count = 0;
+	}
+    }
+
+  return ISIS_OK;
+}
+
+/*
+ * Handle TC notification expiry: send another TC BPDU, up to a hard-coded
+ * limit.
+ */
+static int
+trill_send_tc (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  struct common_bpdu cmb;
+  int retv;
+
+  circuit = THREAD_ARG (thread);
+
+  cmb.cmb_protid = htons (BPDU_PROTID);
+  cmb.cmb_protvers = BPDU_PROTVERS_STP;
+  cmb.cmb_type = BPDU_TYPE_TCNOTIF;
+
+  retv = trill_send_bpdu (circuit, &cmb, sizeof (cmb));
+  if (retv != ISIS_OK)
+    zlog_warn ("TRILL unable to send TC BPDU on %s", circuit->interface->name);
+
+  if (++circuit->tc_count <= 5)
+    {
+      circuit->tc_thread = thread_add_timer (master, trill_send_tc, circuit, 1);
+    }
+  else
+    {
+      circuit->tc_thread = NULL;
+      circuit->tc_count = 0;
+    }
+
+  return retv;
+}
+
+/*
+ * Begin sending TC notification BPDUs on this circuit.  Transmissions are sent
+ * once a second until either 5 have been sent, or we receive a TC Ack from the
+ * root bridge.
+ */
+void
+trill_send_tc_bpdus (struct isis_circuit *circuit)
+{
+  circuit->tc_count = 1;
+  THREAD_TIMER_ON (master, circuit->tc_thread, trill_send_tc, circuit, 1);
+}
diff --git isisd/isis_trilldummy.c isisd/isis_trilldummy.c
new file mode 100644
index 0000000..edaeec3
--- /dev/null
+++ isisd/isis_trilldummy.c
@@ -0,0 +1,54 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_trilldummy.c
+ *
+ * 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 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <vty.h>
+#include <if.h>
+
+#include "dict.h"
+#include "bool.h"
+#include "isis_constants.h"
+#include "isis_common.h"
+#include "isis_flags.h"
+#include "isisd.h"
+#include "isis_adjacency.h"
+#include "isis_circuit.h"
+#include "isis_tlv.h"
+#include "isis_lsp.h"
+#include "isis_vlans.h"
+#include "isis_trill.h"
+
+void trill_read_config (char **cfilep, int argc, char **argv) { }
+void trill_process_hello(struct isis_adjacency *adj, struct list *portcaps) { }
+void trill_nickdb_print (struct vty *vty, struct isis_area *area) { }
+void trill_lspdb_acquire_event(struct isis_circuit *circuit,
+    lspdbacq_state caller) { }
+void trill_nick_destroy(struct isis_lsp *lsp) { }
+void send_trill_vlan_hellos(struct isis_circuit *circuit) { }
+void trill_area_init(struct isis_area *area) { }
+void trill_area_free(struct isis_area *area) { }
+void trill_parse_router_capability_tlvs (struct isis_area *area,
+    struct isis_lsp *lsp) { }
+void trill_process_spf (struct isis_area *area) { }
+int tlv_add_trill_nickname(struct trill_nickname *nick_info,
+    struct stream *stream, struct isis_area *area) { return ISIS_OK; }
+int tlv_add_trill_vlans(struct isis_circuit *circuit) { return ISIS_OK; }
+void install_trill_elements (void) { }
+void install_trill_vlan_elements (void) { }
+int trill_process_bpdu (struct isis_circuit *c, u_char *sa) { return ISIS_OK; }
+char trill_reload(void) { return FALSE; }
diff --git isisd/isis_trillio.c isisd/isis_trillio.c
new file mode 100644
index 0000000..858ba1e
--- /dev/null
+++ isisd/isis_trillio.c
@@ -0,0 +1,301 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_trillio.c
+ *
+ * Copyright (C) 2001,2002    Sampo Saaristo
+ *                            Tampere University of Technology      
+ *                            Institute of Communications Engineering
+ *
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ *
+ * 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 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <net/if_dl.h>
+#include <sys/socket.h>
+#include <sys/stropts.h>
+#include <sys/ethernet.h>
+#include <net/trill.h>
+#include <net/bridge.h>
+
+#include "log.h"
+#include "stream.h"
+#include "network.h"
+#include "if.h"
+#include "vty.h"
+
+#include "isisd/dict.h"
+#include "isisd/include-netbsd/iso.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_network.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
+
+#include "privs.h"
+
+extern struct zebra_privs_t isisd_privs;
+
+static u_char sock_buff[32000];
+
+static const uint8_t all_isis_rbridges[] = ALL_ISIS_RBRIDGES;
+static const uint8_t bridge_group_address[] = BRIDGE_GROUP_ADDRESS;
+
+static int
+open_trill_socket (struct isis_circuit *circuit)
+{
+  struct sockaddr_dl laddr;
+  int fd;
+  unsigned int mtu;
+
+  circuit->fd = -1;
+
+  fd = socket (PF_TRILL, SOCK_DGRAM, 0);
+  if (fd < 0)
+    {
+      zlog_warn ("open_trill_socket(): socket() failed %s",
+		 safe_strerror (errno));
+      return ISIS_ERROR;
+    }
+
+  if (set_nonblocking (fd) < 0)
+    {
+      zlog_warn ("open_trill_socket(): set_nonblocking() failed: %s",
+	  safe_strerror (errno));
+      close (fd);
+      return ISIS_ERROR;
+    }
+
+  if (ioctl (fd, TRILL_NEWBRIDGE, &circuit->area->trill->name) < 0)
+    {
+      zlog_warn ("open_trill_socket(): TRILL_NEWBRIDGE ioctl failed: %s",
+	  safe_strerror (errno));
+      close (fd);
+      return ISIS_ERROR;
+    }
+
+  /*
+   * Bind to the physical interface that must be one of the 
+   * links in the bridge instance.
+   */
+  memset (&laddr, 0, sizeof (struct sockaddr_dl));
+  laddr.sdl_family = AF_TRILL;
+  laddr.sdl_nlen = sizeof (datalink_id_t);
+  *(datalink_id_t *)laddr.sdl_data = circuit->interface->ifindex;
+
+  if (bind (fd, (struct sockaddr *) (&laddr), sizeof (struct sockaddr_dl)) < 0)
+    {
+      zlog_warn ("open_trill_socket(): bind() failed: %s",
+	  safe_strerror (errno));
+      close (fd);
+      return ISIS_ERROR;
+    }
+
+  if (ioctl (fd, TRILL_HWADDR, &circuit->u.bc.snpa) < 0)
+    {
+      zlog_warn ("open_trill_socket(): TRILL_HWADDR ioctl failed: %s",
+	  safe_strerror (errno));
+      close (fd);
+      return ISIS_ERROR;
+    }
+
+  if (ioctl (fd, TRILL_GETMTU, &mtu) < 0)
+    zlog_warn ("open_trill_socket(): TRILL_GETMTU ioctl failed: %s",
+	safe_strerror (errno));
+  else
+    circuit->interface->mtu = mtu;
+
+  if (mtu > sizeof (sock_buff))
+    zlog_err ("open_trill_socket(): interface mtu:%d is greater than "
+        " sock_buff size:%d", mtu, sizeof (sock_buff));
+
+  circuit->fd = fd;
+
+  return ISIS_OK;
+}
+
+/*
+ * Create the socket and set the tx/rx funcs
+ */
+int
+isis_sock_init (struct isis_circuit *circuit)
+{
+  int retval;
+
+  if (isisd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
+
+  circuit->tx = isis_send_pdu_bcast;
+  circuit->rx = isis_recv_pdu_bcast;
+
+  retval = open_trill_socket (circuit);
+
+  if (retval != ISIS_OK)
+    {
+      zlog_warn ("%s: could not initialize the socket", __func__);
+      goto end;
+    }
+
+  if (circuit->circ_type == CIRCUIT_T_P2P)
+    {
+      retval = ISIS_ERROR;
+      zlog_err ("%s: do not support P2P link ", __func__);
+    }
+  else if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+    {
+      zlog_warn ("%s: unknown circuit type", __func__);
+      retval = ISIS_WARNING;
+    }
+
+end:
+  if (isisd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
+
+  return retval;
+}
+
+int
+isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
+{
+  int bytesread, addr_len;
+  struct sockaddr_dl laddr;
+  char *llsaddr;
+  uint16_t tci;
+  uint8_t sap;
+
+  if (circuit->fd == -1)
+    return ISIS_ERROR;
+
+  /* we have to read to the static buff first */
+  addr_len = sizeof (struct sockaddr_dl);
+  bytesread = recvfrom (circuit->fd, sock_buff, sizeof (sock_buff),
+			MSG_DONTWAIT, (struct sockaddr *) &laddr,
+			(socklen_t *) &addr_len);
+
+  if (bytesread < 0 && errno == EWOULDBLOCK)
+    return ISIS_WARNING;
+
+  if (laddr.sdl_slen != sizeof (tci) || laddr.sdl_alen != ETHERADDRL)
+    return ISIS_ERROR;
+
+  if (bytesread < LLC_LEN)
+    return ISIS_WARNING;
+
+  llsaddr = LLADDR(&laddr);
+  memcpy (ssnpa, llsaddr, laddr.sdl_alen);
+  tci = *(uint16_t *)(llsaddr + laddr.sdl_alen);
+
+  sap = tci == TRILL_TCI_BPDU ? ISO_BPDU : ISO_SAP;
+
+  if (sock_buff[0] != sap || sock_buff[1] != sap || sock_buff[2] != 0x03)
+    return ISIS_WARNING;
+
+  circuit->vlans->rx_tci = tci;
+  stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN);
+
+  return ISIS_OK;
+}
+
+int
+isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
+{
+  ssize_t written;
+  size_t msglen;
+  struct sockaddr_dl laddr;
+  char *dp;
+
+  if (circuit->fd == -1)
+    return ISIS_ERROR;
+
+  stream_set_getp (circuit->snd_stream, 0);
+
+  laddr.sdl_family = AF_TRILL;
+  dp = laddr.sdl_data;
+
+  laddr.sdl_nlen = sizeof (datalink_id_t);
+  memcpy (dp, &circuit->interface->ifindex, sizeof (datalink_id_t));
+  dp += laddr.sdl_nlen;
+
+  laddr.sdl_alen = ETHERADDRL;
+  memcpy (dp, all_isis_rbridges, laddr.sdl_alen);
+  dp += laddr.sdl_alen;
+
+  laddr.sdl_slen = sizeof (circuit->vlans->tx_tci);
+  memcpy (dp, &circuit->vlans->tx_tci, laddr.sdl_slen);
+
+  /* now set up the data in the buffer */
+  sock_buff[0] = ISO_SAP;
+  sock_buff[1] = ISO_SAP;
+  sock_buff[2] = 0x03;
+  msglen = stream_get_endp (circuit->snd_stream);
+  if (msglen + LLC_LEN > sizeof (sock_buff))
+    return ISIS_WARNING;
+  stream_get (sock_buff + LLC_LEN, circuit->snd_stream, msglen);
+  msglen += LLC_LEN;
+
+  /* now we can send this */
+  written = sendto (circuit->fd, sock_buff, msglen, 0,
+		    (struct sockaddr *) &laddr, sizeof (struct sockaddr_dl));
+
+  if (written != (ssize_t)msglen)
+    return ISIS_WARNING;
+
+  return ISIS_OK;
+}
+
+int
+trill_send_bpdu (struct isis_circuit *circuit, const void *msg, size_t msglen)
+{
+  ssize_t written;
+  struct sockaddr_dl laddr;
+  char *dp;
+
+  if (circuit->fd == -1)
+    return ISIS_ERROR;
+
+  /* add in the LLC header */
+  sock_buff[0] = ISO_BPDU;
+  sock_buff[1] = ISO_BPDU;
+  sock_buff[2] = 0x03;
+  memcpy (sock_buff + 3, msg, msglen);
+  msglen += 3;
+
+  laddr.sdl_family = AF_TRILL;
+  dp = laddr.sdl_data;
+
+  laddr.sdl_nlen = sizeof (datalink_id_t);
+  memcpy (dp, &circuit->interface->ifindex, sizeof (datalink_id_t));
+  dp += laddr.sdl_nlen;
+
+  laddr.sdl_alen = ETHERADDRL;
+  memcpy (dp, bridge_group_address, laddr.sdl_alen);
+
+  laddr.sdl_slen = 0;
+
+  written = sendto (circuit->fd, sock_buff, msglen, 0,
+		    (struct sockaddr *) &laddr, sizeof (struct sockaddr_dl));
+
+  if (written != (ssize_t)msglen)
+    return ISIS_WARNING;
+
+  return ISIS_OK;
+}
diff --git isisd/isis_trillvlans.c isisd/isis_trillvlans.c
new file mode 100644
index 0000000..2f66c16
--- /dev/null
+++ isisd/isis_trillvlans.c
@@ -0,0 +1,1207 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_trillvlans.c
+ *
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ *
+ * 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 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include <command.h>
+#include <net/trill.h>
+
+#include "linklist.h"
+#include "vty.h"
+#include "dict.h"
+#include "memory.h"
+#include "log.h"
+#include "if.h"
+#include "prefix.h"
+#include "jhash.h"
+#include "stream.h"
+
+#include "isisd/isis_common.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_tlv.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/bool.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_adjacency.h"
+
+static int
+compute_vlan_ranges(uint8_t *vlans, int *vlan, int *start, int *end)
+{
+  int vlan_set;
+  int vlan_start = 0;
+  int prev_vlan = 0;
+
+  EACH_VLAN_R(vlans, *vlan, vlan_set)
+    {
+       if (vlan_start != 0) 
+         {
+	   if (vlan_set) 
+	     {
+               prev_vlan++;
+	       assert (prev_vlan == (*vlan));
+	       continue;
+	     }
+	   *start = vlan_start;
+	   *end = prev_vlan;
+	   return TRUE;
+         }
+       if (!vlan_set)
+         continue;
+       vlan_start = *vlan;
+       prev_vlan = *vlan;
+    }
+  return FALSE;
+}
+
+static void
+trill_del_enabled_vlans_listnode(void *data)
+{
+  XFREE(MTYPE_ISIS_TRILL_ENABLEDVLANS, data); 
+}
+
+static void
+trill_compute_enabled_vlans_subtlv(struct isis_circuit *circuit)
+{
+  unsigned int bytenum;
+  int span = 0;
+  int endspan = 0;
+  int size;
+  uint8_t byte;
+  uint8_t *byteptr;
+  unsigned int foundstartvlan = FALSE;
+  struct list *tlvdatalist;
+  struct trill_enabled_vlans_listnode *data;
+
+  tlvdatalist = list_new();
+  tlvdatalist->del = trill_del_enabled_vlans_listnode;
+
+  for (bytenum=0; bytenum < VLANS_ARRSIZE; bytenum++)
+    {
+       byte = circuit->vlans->enabled[bytenum];
+       if (byte == 0)
+         { 
+	   if (!foundstartvlan)
+             continue;
+	   /* Check for large span, efficient to use a new sub-TLV */
+           if (bytenum != (VLANS_ARRSIZE-1) && ((bytenum - endspan)
+              <= (TLFLDS_LEN + sizeof (struct trill_enabledvlans_subtlv))))
+             continue;
+	 }
+       else if (!foundstartvlan)
+         {
+           foundstartvlan = TRUE;
+	   span = endspan = bytenum;
+	   /* continue checking until we reach end of vlan bit array */
+	   if (bytenum != (VLANS_ARRSIZE-1))
+	     continue;
+         }
+       else 
+         {
+           assert(foundstartvlan);
+           endspan = bytenum;
+	   /* span shouldn't exceed max subtlv length */
+	   if (bytenum != (VLANS_ARRSIZE-1) && (endspan - span) < MAX_VLANS_SUBTLV_LEN)
+             continue;
+	 }
+
+       assert(foundstartvlan);
+       assert(endspan >= span);
+       size = sizeof(struct trill_enabled_vlans_listnode) + endspan - span + 1;
+       data = XMALLOC(MTYPE_ISIS_TRILL_ENABLEDVLANS, size);
+       data->len = size - sizeof(data->len);
+       data->tlvdata.start_vlan = htons(span*NBBY);
+       byteptr = (uint8_t *)&data[1];
+       while (endspan - span >= 0)
+         {
+            assert(byteptr <= (((uint8_t *)data) + size));
+            *byteptr = REVERSE_BYTE(circuit->vlans->enabled[span]);
+	    span++;
+	    byteptr++;
+	 }
+       listnode_add(tlvdatalist, data);
+       foundstartvlan = FALSE;
+    }
+
+  if (listcount(tlvdatalist) > 0)
+    circuit->vlans->enabled_vlans = tlvdatalist;
+  else
+    list_delete(tlvdatalist);
+}
+
+static void
+trill_del_appvlanfwders_listnode(void *data)
+{
+  XFREE(MTYPE_ISIS_TRILL_VLANFWDERS, data); 
+}
+
+static int
+trill_cmp_appvlanfwders(void *data1, void *data2)
+{
+  int vlan1;
+  int vlan2;
+
+  vlan1 = ntohs(((struct appointed_vlanfwder_subtlv *)data1)->vlan_start);
+  vlan2 = ntohs(((struct appointed_vlanfwder_subtlv *)data2)->vlan_start);
+  return (vlan1 < vlan2 ? -1:(vlan1 == vlan2 ? 0:1));
+}
+
+static void
+trill_add_vlanfwder(struct isis_circuit *circuit, int start, int end,
+    int nick, u_int32_t *hash)
+{
+  struct appointed_vlanfwder_subtlv *vlanfwder;
+
+  vlanfwder = XMALLOC (MTYPE_ISIS_TRILL_VLANFWDERS,
+      sizeof (struct appointed_vlanfwder_subtlv)); 
+  vlanfwder->appointee_nick = nick;
+  vlanfwder->vlan_start = htons(start);
+  vlanfwder->vlan_end = htons(end);
+  listnode_add_sort(circuit->vlans->appvlanfwders, vlanfwder);
+  *hash = jhash(vlanfwder, sizeof (struct appointed_vlanfwder_subtlv), *hash);
+}
+
+static void
+trill_compute_vlanfwders(struct isis_circuit *circuit, int *refresh)
+{
+  int vlan = VLAN_MIN;
+  int start;
+  int end;
+  int nick;
+  struct isis_adjacency *adj;
+  struct list *adjdb;
+  struct listnode *node;
+  struct listnode *nextnode;
+  struct appointed_vlanfwder_subtlv *vlanfwder;
+  struct appointed_vlanfwder_subtlv *prevvlanfwder;
+  u_int32_t prevhash = circuit->vlans->vlanfwdershash;
+  u_int32_t newhash = 0;
+
+  if (circuit->vlans->appvlanfwders != NULL) 
+    {
+      list_delete(circuit->vlans->appvlanfwders);
+      circuit->vlans->appvlanfwders = NULL;
+    }
+
+  if (circuit->area->trill->nick.name == RBRIDGE_NICKNAME_NONE)
+    {
+      *refresh = FALSE;
+      return;
+    }
+
+  circuit->vlans->appvlanfwders = list_new();
+  circuit->vlans->appvlanfwders->del = trill_del_appvlanfwders_listnode;
+  circuit->vlans->appvlanfwders->cmp = trill_cmp_appvlanfwders;
+
+  /*
+   * From the assigned VLAN forwarders among the adjacencies compute
+   * appointed VLAN forwarder sub-TLVs. We exclude VLANs assigned to
+   * ourself (the DR).
+   */
+  adjdb = circuit->u.bc.adjdb[TRILL_ISIS_LEVEL - 1];
+  for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
+    {
+      vlan = VLAN_MIN;
+      while (compute_vlan_ranges(adj->vlans->forwarder, &vlan, &start, &end))
+        { 
+          nick = sysid_to_nick(circuit->area, adj->sysid);
+	  if (nick != RBRIDGE_NICKNAME_NONE)
+            trill_add_vlanfwder(circuit, start, end, nick, &newhash);
+	}
+    }
+
+  circuit->vlans->vlanfwdershash = newhash;
+  *refresh = (newhash == prevhash ? FALSE:TRUE);
+
+  /*
+   * Compress the VLAN forwarder sub-TLVs by including missing VLANs in
+   * the ranges. We use the sorted appvlanfwders list to quickly determine
+   * the missing VLANs.
+   */
+  nick = 0;
+  prevvlanfwder = NULL;
+  for (ALL_LIST_ELEMENTS(circuit->vlans->appvlanfwders, node,
+      nextnode, vlanfwder))
+    {
+      if (nick != 0 && vlanfwder->appointee_nick == nick)
+        {
+          prevvlanfwder->vlan_end = vlanfwder->vlan_end;
+          trill_del_appvlanfwders_listnode(vlanfwder);
+          list_delete_node(circuit->vlans->appvlanfwders, node);
+	  continue;
+        }
+       nick = vlanfwder->appointee_nick;
+       prevvlanfwder = vlanfwder;
+    }
+}
+
+/*
+ * Clear all our info on VLAN forwarders. 
+ */
+static void
+trill_clear_vlanfwderinfo(struct isis_circuit *circuit)
+{
+  struct listnode *node;
+  struct isis_adjacency *adj;
+
+  /* Clear existing VLAN forwarder information */
+  memset (circuit->vlans->forwarder, 0, VLANS_ARRSIZE);
+
+  for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[TRILL_ISIS_LEVEL-1], node, adj))
+    memset (adj->vlans->forwarder, 0, VLANS_ARRSIZE);
+}
+
+/*
+ * TRILL function called when sending a hello frame on a TRILL circuit.
+ * Sends additional VLAN Hellos for TRILL based on VLANs we see Hellos on
+ * and from VLANs reported by other adjacencies. If we are DR then VLAN 
+ * forwarders are also computed. 
+ */
+void
+send_trill_vlan_hellos(struct isis_circuit *circuit)
+{
+  struct listnode *node;
+  struct list *adjdb;
+  struct isis_adjacency *adj;
+  u_int8_t txvlans[VLANS_ARRSIZE];
+  u_int8_t fwdvlans[VLANS_ARRSIZE];
+  u_int vlan_set;
+  int vlan;
+
+  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+    return;
+
+  if (circuit->u.bc.is_dr[TRILL_ISIS_LEVEL - 1])
+    {
+      int refresh_lsp;
+
+      /* Update circuit's designated VLAN */
+      circuit->vlans->designated = circuit->vlans->our_designated;
+
+      trill_clear_vlanfwderinfo(circuit);
+
+      /* Appoint ourselves the VLAN forwarder for all our enabled VLANs */
+      memcpy(circuit->vlans->forwarder, circuit->vlans->enabled, VLANS_ARRSIZE);
+
+      /* Initialize the list of VLANs already assigned VLAN forwarder */
+      memcpy(fwdvlans, circuit->vlans->enabled, VLANS_ARRSIZE);
+
+      adjdb = circuit->u.bc.adjdb[TRILL_ISIS_LEVEL - 1];
+      for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
+        {
+          /* 
+           * If DR then appoint VLAN forwarder if no RBridge
+           * has been appointed yet to forward the particular VLAN.
+           */
+          EACH_VLAN_SET(adj->vlans->enabled, vlan, vlan_set)
+	   {
+              if (CHECK_VLAN(fwdvlans, vlan))
+                continue;
+
+	      /* 
+	       * Delegate the VLAN forwarding to the adjacency
+	       * as no other RBridge is forwarding the VLAN.
+	       */
+              SET_VLAN(adj->vlans->forwarder, vlan);
+	      SET_VLAN(fwdvlans, vlan);
+	   }
+        }
+
+      /*
+       * Based on the above VLAN forwarder appointments compute the VLAN
+       * forwarder TLVs. If VLAN forwarder info has changed then we also
+       * generate new LSPs.
+       */ 
+      trill_compute_vlanfwders(circuit, &refresh_lsp);
+      if (refresh_lsp)
+        lsp_regenerate_schedule (circuit->area);
+
+      /* DR sends hellos on all its enabled VLANs */
+      memcpy(txvlans, circuit->vlans->enabled, VLANS_ARRSIZE);
+      SET_VLAN(txvlans, circuit->vlans->designated);
+    }
+  else 
+    {
+      /*
+       * Non-DR sends hellos on designated VLAN (if enabled)
+       * and on all VLANs it is the appointed forwarder.
+       */
+      bzero(txvlans, VLANS_ARRSIZE);
+      if (CHECK_VLAN(circuit->vlans->enabled, circuit->vlans->designated))
+        SET_VLAN(txvlans, circuit->vlans->designated);
+      MERGE_VLANS(txvlans, vlan, circuit->vlans->forwarder);
+    }
+
+  /* Send hellos */
+  EACH_VLAN_SET(txvlans, vlan, vlan_set)
+    {
+      circuit->vlans->tx_tci = vlan;
+      send_hello(circuit, TRILL_ISIS_LEVEL);
+    }
+
+  /* Re-set circuit to use the link's designated VLAN for all IS-IS frames */
+  circuit->vlans->tx_tci = VLANTCI(circuit->vlans->designated);
+
+  /* Compute enabled VLANs subtlvs (performed only once) */
+  if (circuit->vlans->enabled_vlans == NULL)
+    trill_compute_enabled_vlans_subtlv(circuit);
+}
+
+static void
+trill_del_vlansreachablelist(void *obj)
+{
+  XFREE (MTYPE_ISIS_TRILL_VLANSREACHABLE, obj);
+}
+
+/*
+ * Compute VLAN filter lists by recursively going over the nodes in the DT.
+ * If rvertex is set then we stop at the matching node in the DT otherwise
+ * we stop until all children nodes are covered.
+ */
+static void
+trill_compute_vlanfilterlist(struct isis_area *area, struct isis_spftree *rdtree,
+  struct isis_vertex *vertex, struct isis_vertex *rvertex, uint8_t *filtermap)
+{
+  nicknode_t *nicknode;
+  struct isis_vertex *cvertex;
+  struct listnode *node;
+  int idx;
+
+  if (!listnode_lookup (rdtree->paths, vertex))
+    return;
+  if (vertex->type != VTYPE_NONPSEUDO_IS &&
+      vertex->type != VTYPE_NONPSEUDO_TE_IS)
+    return;
+
+  nicknode = trill_nicknode_lookup(area, sysid_to_nick(area, vertex->N.id));
+  if (nicknode == NULL)
+	  return;
+
+  MERGE_VLANS(filtermap, idx, nicknode->info.vlans_forwarder);
+
+  if (rvertex != NULL &&
+      (memcmp(vertex->N.id, rvertex->N.id, ISIS_SYS_ID_LEN) == 0))
+    return;
+
+  if (vertex->children != NULL)
+    {
+       for (ALL_LIST_ELEMENTS_RO(vertex->children, node, cvertex))
+         trill_compute_vlanfilterlist(area, rdtree, cvertex,
+		 rvertex, filtermap);
+    }
+}
+
+/*
+ * Creates TRILL VLAN filter lists for each of our adjacencies on 
+ * the given node's distribution tree (DT). A TRILL VLAN filter list
+ * for an adjacency on a distribution tree is the set of all VLANs that
+ * are reachable downstream via the adjacency.
+ */
+void
+trill_create_vlanfilterlist(struct isis_area *area, nicknode_t *nicknode)
+{
+  struct listnode *node;
+  struct listnode *lnode;
+  struct list *adjlist;
+  struct isis_vertex *vertex;
+  struct isis_vertex *rbvertex;
+  struct isis_vertex *adjvertex;
+  struct isis_spftree *rdtree;
+  void *listdata;
+  u_int16_t adjnick;
+  nicknode_t *adjnode;
+  int adjishead;
+  struct list *vlanfilterlist;
+  uint8_t *vlanfiltermap;
+
+  if (nicknode == NULL) 
+   {
+     adjlist = area->trill->adjnodes;
+     rdtree = area->spftree[TRILL_ISIS_LEVEL-1];
+     if (area->trill->vlans_reachable != NULL)
+       {
+         list_delete(area->trill->vlans_reachable);
+	 area->trill->vlans_reachable = NULL;
+       }
+   }
+  else
+   {
+     adjlist = nicknode->adjnodes;
+     rdtree = nicknode->rdtree;
+     if (nicknode->vlans_reachable != NULL)
+       {
+         list_delete(nicknode->vlans_reachable);
+	 nicknode->vlans_reachable = NULL;
+       }
+   }
+  
+  if (adjlist == NULL)
+    return;
+
+  vlanfilterlist = list_new();
+  vlanfilterlist->del = trill_del_vlansreachablelist;
+
+  /*
+   * For each of the adjacencies compute VLAN filter list
+   * on the DT with nicknode as the root.
+   */
+  for (ALL_LIST_ELEMENTS_RO (adjlist, node, listdata))
+    {
+      adjnick = (u_int16_t)(u_long)listdata;
+      adjnode = trill_nicknode_lookup(area, adjnick);
+      if (adjnode == NULL)
+        {
+          zlog_warn("trill_create_vlanfilterlist: adjlist node lookup failed.");
+          list_delete(vlanfilterlist);
+	  return;
+	}
+
+      /*
+       * Determine if the adjacency is towards the parent (adjishead is TRUE)
+       * or if the adjacency is our child node on the DT with nicknode as root.
+       * Computing this direction determines how we search for reachable VLANs.
+       */
+      adjishead = FALSE;
+      rbvertex = adjvertex = NULL;
+      for (ALL_LIST_ELEMENTS_RO (rdtree->paths, lnode, vertex))
+        {
+          if (vertex->type != VTYPE_NONPSEUDO_IS &&
+	      vertex->type != VTYPE_NONPSEUDO_TE_IS)
+	    continue;
+	  /* We found the adjacency node in the tree */
+	  if (memcmp (vertex->N.id, adjnode->info.sysid, ISIS_SYS_ID_LEN) == 0)
+            adjvertex = vertex;
+	  /* We found our node in the DT with nicknode as root */
+	  else if (memcmp (vertex->N.id, area->isis->sysid,
+	      ISIS_SYS_ID_LEN) == 0)
+	    rbvertex = vertex;
+	  else
+            continue;
+	  /* If we found adjacency node first then we set adjishead to TRUE */
+	  if (!adjishead  && adjvertex != NULL && rbvertex == NULL)
+             adjishead = TRUE;
+        }
+
+      if (rbvertex == NULL || adjvertex == NULL)
+        {
+          zlog_warn("trill_create_vlanfilterlist: rbvertex adjvertex lookup failed.");
+          list_delete(vlanfilterlist);
+	  return;
+        }
+
+      vlanfiltermap = XCALLOC(MTYPE_ISIS_TRILL_VLANSREACHABLE, VLANS_ARRSIZE);
+      if (adjishead == TRUE)
+        {
+	  /*
+	   * If adjacency is head then compute VLAN filter lists from the root
+	   * node and cover all nodes except all the children of the adjacency
+	   * node. This covers all nodes in the tree except the adjacency
+	   * branch.
+	   */
+          trill_compute_vlanfilterlist(area, rdtree,
+		listgetdata(listhead (rdtree->paths)), adjvertex, vlanfiltermap);
+        }
+      else
+        {
+	  /*
+	   * Adjacency is a child node of ours in the DT so to compute all the VLANs
+	   * reachable through the child we just go over all the children nodes.
+	   */
+          trill_compute_vlanfilterlist(area, rdtree, adjvertex, NULL, vlanfiltermap);
+        }
+      listnode_add(vlanfilterlist, vlanfiltermap);
+    }
+
+  /* Must compute a VLAN filter map per adjacency */
+  if (listcount(vlanfilterlist) == listcount(adjlist))
+    {
+       if (nicknode == NULL)
+         area->trill->vlans_reachable = vlanfilterlist;
+       else
+         nicknode->vlans_reachable = vlanfilterlist;
+    }
+}
+
+static void
+trill_parse_enabled_vlans_subtlv(struct isis_adjacency *adj, u_int8_t *ptr,
+	u_int8_t len)
+{
+   u_int8_t *end;
+   struct trill_enabledvlans_subtlv *vlanmap;
+   int vlan;
+   u_int8_t byte;
+   int idx;
+
+   end = ptr + len;
+   vlanmap = (struct trill_enabledvlans_subtlv *)ptr;
+   vlan = VLANTCI(ntohs(vlanmap->start_vlan));
+
+   ptr += sizeof (struct trill_enabledvlans_subtlv);
+   while (ptr < end)
+     {
+        byte = *ptr++;
+	if (byte == 0)
+	  {
+            vlan += NBBY;
+            continue;
+	  }
+
+	for (idx = NBBY-1; idx >= 0; idx--)
+	  {
+            if ((byte & (1<<idx)) != 0)
+              SET_VLAN(adj->vlans->enabled, vlan);
+            vlan++;
+	  }
+     }
+}
+
+static void
+inhibit_free(void *arg)
+{
+  XFREE (MTYPE_ISIS_TRILL_INHIB, arg);
+}
+
+static void
+remove_inhib(struct isis_circuit *circuit, u_int16_t rxvlan)
+{
+  struct trill_inhibit_vlan *inhib;
+  struct listnode *node, *nextnode;
+  struct trill_circuit_vlans *cvlans = circuit->vlans;
+
+  for (ALL_LIST_ELEMENTS (cvlans->inhibit_vlans, node, nextnode, inhib))
+    {
+      if (inhib->vlan == rxvlan)
+	{
+	  list_delete_node (cvlans->inhibit_vlans, node);
+	  inhibit_free (inhib);
+	}
+    }
+  if (list_isempty (cvlans->inhibit_vlans) && cvlans->inhibit_all == 0 &&
+      cvlans->inhibit_thread != NULL)
+    {
+      thread_cancel (cvlans->inhibit_thread);
+      cvlans->inhibit_thread = NULL;
+    }
+}
+
+/*
+ * Update the previous and new lists of VLANs for which we're the appointed
+ * forwarder, based on the inhibited VLAN list.
+ *
+ * If the VLAN is set in the new list, then the inhibiting entry is still
+ * valid; clear it from that new list.
+ *
+ * If the VLAN is not set in the new list, then the DR has revoked our
+ * appointment, so the entry must be removed from the inhibit list, and treated
+ * as though it were previously enabled.
+ */
+static void
+check_disabled_inhib (struct isis_circuit *circuit, u_int8_t *prevvlans,
+    u_int8_t *newvlans)
+{
+  struct trill_inhibit_vlan *inhib;
+  struct listnode *node, *nextnode;
+  struct trill_circuit_vlans *cvlans = circuit->vlans;
+
+  for (ALL_LIST_ELEMENTS (cvlans->inhibit_vlans, node, nextnode, inhib))
+    {
+      if (CHECK_VLAN (newvlans, inhib->vlan))
+	{
+	  CLEAR_VLAN (newvlans, inhib->vlan);
+	}
+      else
+	{
+	  SET_VLAN (prevvlans, inhib->vlan);
+	  list_delete_node (cvlans->inhibit_vlans, node);
+	  inhibit_free (inhib);
+	}
+    }
+  if (list_isempty (cvlans->inhibit_vlans) && cvlans->inhibit_all == 0 &&
+      cvlans->inhibit_thread != NULL)
+    {
+      thread_cancel (cvlans->inhibit_thread);
+      cvlans->inhibit_thread = NULL;
+    }
+}
+
+static int
+uninhibit_vlan (struct thread *thread)
+{
+  struct isis_circuit *circuit;
+  struct trill_circuit_vlans *cvlans;
+  struct listnode *node = NULL;
+  struct trill_inhibit_vlan *inhib = NULL;
+  int mintime, alltime;
+  char reenabled = FALSE;
+  time_t now;
+
+  circuit = THREAD_ARG (thread);
+  cvlans = circuit->vlans;
+
+  now = time (NULL);
+  alltime = cvlans->inhibit_all - now;
+  if (cvlans->inhibit_all != 0 && alltime <= 0)
+    {
+      cvlans->inhibit_all = 0;
+      reenabled = TRUE;
+    }
+
+  if (cvlans->inhibit_vlans != NULL)
+    {
+      while ((node = listhead (cvlans->inhibit_vlans)) != NULL)
+	{
+	  inhib = listgetdata (node);
+	  if ((int)(inhib->reenable - now) > 0)
+	    break;
+	  reenabled = TRUE;
+	  SET_VLAN (cvlans->forwarder, inhib->vlan);
+	  list_delete_node (cvlans->inhibit_vlans, node);
+	  inhibit_free (inhib);
+	}
+    }
+
+  /* If we've reenabled something, then tell the kernel */
+  if (reenabled && cvlans->inhibit_all == 0)
+    trill_set_vlan_forwarder (circuit, cvlans->forwarder);
+
+  /* Set up the next expiry */
+  if (node == NULL && cvlans->inhibit_all == 0)
+    cvlans->inhibit_thread = NULL;
+  else
+    {
+      mintime = node == NULL ? alltime : inhib->reenable - now;
+      if (cvlans->inhibit_all != 0 && alltime < mintime)
+	mintime = alltime;
+      cvlans->inhibit_thread = thread_add_timer (master, uninhibit_vlan,
+	  circuit, mintime);
+    }
+  return ISIS_OK;
+}
+
+void
+trill_inhib_all(struct isis_circuit *circuit)
+{
+  u_int8_t nullvlans[VLANS_ARRSIZE];
+  struct trill_circuit_vlans *cvlans = circuit->vlans;
+  int interval;
+
+  memset (nullvlans, 0, sizeof nullvlans);
+  trill_set_vlan_forwarder (circuit, nullvlans);
+
+  interval = 15;
+  cvlans->inhibit_all = time (NULL) + interval;
+
+  THREAD_TIMER_ON (master, cvlans->inhibit_thread, uninhibit_vlan, circuit,
+      interval);
+}
+
+static void
+add_inhib(struct isis_circuit *circuit, u_int16_t rxvlan)
+{
+  struct trill_circuit_vlans *cvlans = circuit->vlans;
+  struct trill_inhibit_vlan *inhib;
+  int interval;
+
+  interval = 5 * circuit->hello_interval[0];
+
+  inhib = XMALLOC (MTYPE_ISIS_TRILL_INHIB, sizeof (*inhib));
+  inhib->vlan = rxvlan;
+  inhib->reenable = time (NULL) + interval;
+  listnode_add (cvlans->inhibit_vlans, inhib);
+
+  CLEAR_VLAN (cvlans->forwarder, rxvlan);
+  if (cvlans->inhibit_all == 0)
+    trill_set_vlan_forwarder (circuit, cvlans->forwarder);
+
+  THREAD_TIMER_ON (master, cvlans->inhibit_thread, uninhibit_vlan, circuit,
+      interval);
+}
+
+/*
+ * Process incoming hello packets and process port capability TLVs.
+ */
+void
+trill_process_hello(struct isis_adjacency *adj, struct list *portcaps)
+{
+  u_int8_t subtlv;
+  u_int8_t *ptr;
+  int len;
+  int subtlv_len;
+  struct listnode *node;
+  struct port_capability_tlv *pcap;
+  struct trill_vlanflags_subtlv *vlanflags = NULL;
+  struct isis_circuit *circuit = adj->circuit;
+  struct trill_circuit_vlans *cvlans = circuit->vlans;
+  struct list *vlanfwders = NULL;
+  int vflags_stlv_found = FALSE;
+  int adj_is_dr = FALSE;
+  int dis_nick = RBRIDGE_NICKNAME_NONE;
+
+  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+    return;
+
+  if ((!circuit->u.bc.is_dr[TRILL_ISIS_LEVEL - 1]) &&
+        memcmp(circuit->u.bc.l1_desig_is, adj->sysid, ISIS_SYS_ID_LEN) == 0) {
+    adj_is_dr = TRUE;
+    dis_nick = sysid_to_nick(circuit->area, adj->sysid);
+  }
+
+  memset(adj->vlans->enabled, 0, VLANS_ARRSIZE);
+  for (ALL_LIST_ELEMENTS_RO (portcaps, node, pcap))
+    {
+       len = pcap->len;
+       ptr = pcap->value;
+       while (len > TLFLDS_LEN)
+         {
+            subtlv = *ptr; ptr++; len--;
+	    subtlv_len = *ptr; ptr++; len--;
+	    if (subtlv_len > len)
+	      break;
+
+	    switch (subtlv)
+	      {
+	      case PCSTLV_VLANS:
+	         if (vflags_stlv_found == TRUE)
+                   {
+		     zlog_warn("trill_process_hello: received more than"
+			    " one VLANs and Flags sub-TLV");
+		     vlanflags = NULL;
+		   }
+		 else if (subtlv_len == PCSTLV_VLANS_LEN && vlanflags == NULL)
+		   vlanflags = (struct trill_vlanflags_subtlv *)ptr;
+		 len -= subtlv_len;
+		 ptr += subtlv_len;
+		 vflags_stlv_found = TRUE;
+	         break;
+
+	      case PCSTLV_APPFORWARDERS:
+		 if ((subtlv_len % sizeof (struct appointed_vlanfwder_subtlv))
+		     != 0)
+		   {
+		     zlog_warn("trill_process_hello: received invalid length:%d"
+			" appointed forwarders sub-TLV", subtlv_len);
+		     len -= subtlv_len;
+		     ptr += subtlv_len;
+		     break;
+		   }
+		 if (vlanfwders == NULL)
+		   vlanfwders = list_new();
+		 while (subtlv_len > 0)
+		   {
+		      listnode_add (vlanfwders, ptr);
+		      ptr += sizeof (struct appointed_vlanfwder_subtlv);
+		      subtlv_len -= sizeof (struct appointed_vlanfwder_subtlv);
+		      len -= sizeof (struct appointed_vlanfwder_subtlv);
+		   }
+		 break;
+
+	      case PCSTLV_ENABLEDVLANS:
+	        if (subtlv_len < PCSTLV_ENABLEDVLANS_MIN_LEN)	
+		  zlog_warn("trill_process_hello:"
+		     " received invalid length (too small):%d"
+		     " enabled VLANS sub-TLV", subtlv_len);
+		else
+		  trill_parse_enabled_vlans_subtlv(adj, ptr, subtlv_len);
+	        len -= subtlv_len;
+	        ptr += subtlv_len;
+		break;
+
+              default:
+		 len -= subtlv_len;
+		 ptr += subtlv_len;
+		 break;
+	      }
+	 }
+   }
+
+  /* Process appointed VLAN forwarders sub-TLV */
+  if (adj_is_dr)
+    {
+      u_int8_t *fwdvlans;
+      u_int8_t *enabledvlans;
+      u_int8_t prevfwdvlans[VLANS_ARRSIZE];
+      u_int8_t appvlans[VLANS_ARRSIZE];
+      struct appointed_vlanfwder_subtlv *appvlanfwder;
+      struct isis_adjacency *nadj;
+      int vlan;
+      int vbyte;
+      int vlanstart;
+      int vlanend;
+      u_char *sysid;
+
+      memcpy(prevfwdvlans, cvlans->forwarder, VLANS_ARRSIZE);
+      bzero(appvlans, sizeof (appvlans));
+
+      /* Clear existing VLAN forwarder information */
+      trill_clear_vlanfwderinfo(circuit);
+
+      if (vlanfwders != NULL)
+	for (ALL_LIST_ELEMENTS_RO (vlanfwders, node, appvlanfwder))
+        {
+	   /* Disregard any appointed VLAN forwarders to the DR */
+           if (appvlanfwder->appointee_nick == dis_nick)
+             continue;
+
+           if (appvlanfwder->appointee_nick == circuit->area->trill->nick.name)
+             {
+               sysid = circuit->area->isis->sysid;
+	       fwdvlans = cvlans->forwarder;
+	       enabledvlans = cvlans->enabled;
+	     }
+	   else
+	     {
+	       sysid = nick_to_sysid (circuit->area, appvlanfwder->appointee_nick);
+	       if (!sysid)
+                 continue;
+	       if ((nadj = isis_adj_lookup (sysid,
+	           circuit->u.bc.adjdb[TRILL_ISIS_LEVEL-1])) == NULL)
+	         continue;
+	       fwdvlans = nadj->vlans->forwarder;
+	       enabledvlans = nadj->vlans->enabled;
+	     }
+
+	   vlanstart = VLANTCI(ntohs(appvlanfwder->vlan_start));
+	   vlanend = VLANTCI(ntohs(appvlanfwder->vlan_end));
+
+	   /* Only accept VLANs the RBridge has advertised as enabled */
+	   for (vlan = vlanstart; vlan <= vlanend; vlan++)
+             if (CHECK_VLAN(enabledvlans, vlan)) 
+	       {
+                 SET_VLAN (fwdvlans, vlan);
+                 SET_VLAN (appvlans, vlan);
+	       }
+        }
+
+      /*
+       * Determine the VLANs forwarded by the adj that is the DR: they are
+       * all the VLANs enabled in the DR minus the VLANs that have appointed
+       * VLAN forwarders on the link.
+       */
+      for (vbyte = 0; vbyte < VLANS_ARRSIZE; vbyte++)
+        adj->vlans->forwarder[vbyte] =
+	    adj->vlans->enabled[vbyte] & ~appvlans[vbyte];
+
+      /*
+       * If there are any inhibited VLANs, then check whether we've lost AF
+       * status for them.  If so, then remove the inhibiting entry; it's no
+       * longer valid.  If not, then remove from new forwarder list.
+       */
+      if (cvlans->inhibit_vlans != NULL)
+	check_disabled_inhib (circuit, prevfwdvlans, cvlans->forwarder);
+
+      /*
+       * If the set of VLANs for which we've been appointed as forwarder has
+       * changed, then regenerate new LSPs with new AF bits and deal with AF
+       * status changes.
+       */
+      if (memcmp (prevfwdvlans, cvlans->forwarder, VLANS_ARRSIZE))
+	{
+	  int lost_any, vbit;
+	  u_int8_t vval;
+	  struct isis_circuit *ocir;
+
+	  /*
+	   * Compute the set of VLANs for which we're forwarder for some other
+	   * circuit.
+	   */
+	  bzero (appvlans, sizeof (appvlans));
+	  for (ALL_LIST_ELEMENTS_RO (circuit->area->circuit_list, node, ocir))
+	    {
+	      if (ocir != circuit)
+		{
+		  for (vbyte = 0; vbyte < VLANS_ARRSIZE; vbyte++)
+		    appvlans[vbyte] |= ocir->vlans->forwarder[vbyte];
+		}
+	    }
+
+	  /*
+	   * For all VLANs where we've lost AF status, increment the lost
+	   * counter and flush bridge forwarding entries learned directly over
+	   * this circuit for this VLAN.
+	   */
+	  lost_any = FALSE;
+	  for (vbyte = 0; vbyte < VLANS_ARRSIZE; vbyte++)
+	    {
+	      vval = prevfwdvlans[vbyte] & ~cvlans->forwarder[vbyte];
+	      if (vval != 0)
+		{
+		  lost_any = TRUE;
+		  for (vbit = 0; vbit < 8; vbit++)
+		    {
+		      if (vval & (1 << vbit))
+			{
+			  vlan = vbyte * 8 + vbit;
+			  trill_port_flush (circuit, vlan);
+			  if (!CHECK_VLAN (appvlans, vlan))
+			    trill_nick_flush (circuit, vlan);
+			}
+		    }
+		}
+	    }
+	  if (lost_any)
+	    {
+	      /* XXX carlsonj bump lost counter here */
+	      trill_send_tc_bpdus (circuit);
+	    }
+	  lsp_regenerate_schedule (circuit->area);
+	}
+    }
+
+  if (vlanflags != NULL)
+    {
+      int outervlan, rxvlan;
+
+      /*
+       * First get the flags stored in outer_vlan.  Check for a conflict if
+       * we've been set as the appointed forwarder.
+       */
+      outervlan = ntohs (vlanflags->outer_vlan);
+      rxvlan = VLANTCI (cvlans->rx_tci);
+      if ((outervlan & TVFFO_AF) && CHECK_VLAN (cvlans->forwarder, rxvlan))
+	{
+	  /* The inhibited VLANs list is created just once; it's rare */
+	  if (cvlans->inhibit_vlans == NULL)
+	    {
+	      cvlans->inhibit_vlans = list_new ();
+	      cvlans->inhibit_vlans->del = inhibit_free;
+	    }
+	  /* Remove any stale entries for this VLAN. */
+	  remove_inhib (circuit, rxvlan);
+	  /* Now add a new entry for the VLAN */
+	  add_inhib (circuit, rxvlan);
+	}
+
+      adj->vlans->designated = VLANTCI(ntohs(vlanflags->desig_vlan));
+      outervlan = VLANTCI(outervlan);
+      SET_VLAN(adj->vlans->seen, outervlan);
+      SET_VLAN(adj->vlans->seen, VLANTCI(cvlans->rx_tci));
+
+      /* If Adj is DR set circuit's designated link */
+      if (adj_is_dr)
+        {
+	  cvlans->designated = adj->vlans->designated;
+	  cvlans->tx_tci = VLANTCI(cvlans->designated);
+	}
+    }
+  if (vlanfwders != NULL)
+    list_delete (vlanfwders);
+}
+
+/* Add TRILL VLAN TLVs in TRILL IS-IS hellos */
+int
+tlv_add_trill_vlans(struct isis_circuit *circuit)
+{
+  struct stream *stream = circuit->snd_stream;
+  struct trill_vlanflags_subtlv vlanflags;
+  uint16_t outervlan;
+  struct listnode *node;
+  size_t tlvstart;
+  struct trill_enabled_vlans_listnode *evlans;
+  struct appointed_vlanfwder_subtlv *vlanfwder;
+  int rc;
+
+  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
+    return ISIS_OK;
+
+  tlvstart = stream_get_endp (stream);
+  rc = add_tlv(PORT_CAPABILITY, 0, NULL, stream);
+  if (rc != ISIS_OK)
+    return rc;
+
+  outervlan = VLANTCI(circuit->vlans->tx_tci);
+  if (CHECK_VLAN(circuit->vlans->forwarder, outervlan))
+    outervlan |= TVFFO_AF;
+  vlanflags.outer_vlan = htons(outervlan);
+  vlanflags.desig_vlan = htons(circuit->vlans->designated);
+  rc = add_subtlv (PCSTLV_VLANS, sizeof (vlanflags), (uchar_t *)&vlanflags,
+      tlvstart, stream);
+  if (rc != ISIS_OK)
+    return rc;
+
+  if (circuit->vlans->enabled_vlans != NULL)
+    {
+       for (ALL_LIST_ELEMENTS_RO(circuit->vlans->enabled_vlans, node, evlans))
+         {
+            rc = add_subtlv(PCSTLV_ENABLEDVLANS, evlans->len,
+               (uchar_t *)&evlans->tlvdata, tlvstart, stream);
+	    if (rc == ISIS_ERROR)
+               return rc;
+	    if (rc == ISIS_WARNING)
+	      {
+                 tlvstart = stream_get_endp (stream);
+                 rc = add_tlv(PORT_CAPABILITY, 0, NULL, stream);
+                 if (rc != ISIS_OK)
+                   return rc;
+                 rc = add_subtlv(PCSTLV_ENABLEDVLANS, evlans->len,
+                    (uchar_t *)&evlans->tlvdata, tlvstart, stream);
+                 if (rc != ISIS_OK)
+                   return rc;
+	      }
+         }
+    }
+
+  if (!circuit->u.bc.is_dr[TRILL_ISIS_LEVEL - 1] ||
+       circuit->vlans->appvlanfwders == NULL)
+      return rc;
+
+  for (ALL_LIST_ELEMENTS_RO(circuit->vlans->appvlanfwders, node,
+      vlanfwder))
+    {
+       rc = add_subtlv(PCSTLV_APPFORWARDERS, sizeof(*vlanfwder),
+          (uchar_t *)vlanfwder, tlvstart, stream);
+       if (rc == ISIS_ERROR)
+          return rc;
+       if (rc == ISIS_WARNING)
+         {
+           tlvstart = stream_get_endp (stream);
+           rc = add_tlv(PORT_CAPABILITY, 0, NULL, stream);
+           if (rc != ISIS_OK)
+             return rc;
+           rc = add_subtlv(PCSTLV_APPFORWARDERS, sizeof(*vlanfwder),
+              (uchar_t *)vlanfwder, tlvstart, stream);
+           if (rc != ISIS_OK)
+             return rc;
+	 }
+    }
+  return rc;
+}
+
+/*
+ * show trill circuits command to display TRILL circuit information.
+ */
+void
+trill_circuits_print_all (struct vty *vty, struct isis_area *area)
+{
+  struct listnode *node;
+  struct isis_circuit *circuit;
+  int vlan_count = 0;
+  int vlan_set;
+  int vlan;
+
+  if (area->circuit_list == NULL)
+    return;
+
+  for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
+    {
+
+      vty_out (vty, "%sInterface %s:%s", VTY_NEWLINE, circuit->interface->name, VTY_NEWLINE);
+      vty_out (vty, "PVID:%d  Our Designated VLAN:%d Designated VLAN:%d%s",
+	 circuit->vlans->pvid, circuit->vlans->designated, circuit->vlans->our_designated,
+	 VTY_NEWLINE);
+
+      vty_out (vty, "VLAN Forwarder: ");
+      EACH_VLAN_SET(circuit->vlans->forwarder, vlan, vlan_set)
+        {
+	   vlan_count++;
+	   if (vlan_count % 8 == 0)
+             vty_out(vty, "%s               ", VTY_NEWLINE);
+           vty_out (vty, "%d ", vlan); 
+        }
+      vty_out (vty, "%sEnabled VLANs: ", VTY_NEWLINE);
+      vlan_count = 0;
+      EACH_VLAN_SET(circuit->vlans->enabled, vlan, vlan_set)
+        {
+	   vlan_count++;
+	   if (vlan_count % 8 == 0)
+             vty_out(vty, "%s               ", VTY_NEWLINE);
+           vty_out (vty, "%d ", vlan); 
+        }
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+DEFUN (trill_isis_vlan,
+       trill_isis_vlan_cmd,
+       "trill isis vlan <1-4094>",
+       "TRILL IS-IS commands\n"
+       "Set TRILL IS-IS VLAN\n"
+       "VLAN ID\n")
+{
+  struct isis_circuit *circuit;
+  struct interface *ifp;
+
+  ifp = vty->index;
+  circuit = ifp->info;
+  if (circuit == NULL)
+    {
+      return CMD_WARNING;
+    }
+  assert (circuit);
+
+  SET_VLAN(circuit->vlans->enabled, atoi(argv[0]));
+  return CMD_SUCCESS;
+}
+
+DEFUN (trill_isis_no_vlan,
+       trill_isis_no_vlan_cmd,
+       "trill isis no vlan <1-4094>",
+       "TRILL IS-IS commands\n"
+       "Clear TRILL IS-IS VLAN\n"
+       "VLAN ID\n")
+{
+  struct isis_circuit *circuit;
+  struct interface *ifp;
+
+  ifp = vty->index;
+  circuit = ifp->info;
+  if (circuit == NULL)
+    {
+      return CMD_WARNING;
+    }
+  assert (circuit);
+
+  CLEAR_VLAN(circuit->vlans->enabled, atoi(argv[0]));
+  return CMD_SUCCESS;
+}
+
+DEFUN (trill_isis_pvid,
+       trill_isis_pvid_cmd,
+       "trill isis pvid <1-4094>",
+       "TRILL IS-IS commands\n"
+       "Set TRILL IS-IS Native VLAN (PVID) \n"
+       "PVID\n")
+{
+  struct isis_circuit *circuit;
+  struct interface *ifp;
+
+  ifp = vty->index;
+  circuit = ifp->info;
+  if (circuit == NULL)
+    {
+      return CMD_WARNING;
+    }
+  assert (circuit);
+
+  circuit->vlans->pvid = atoi(argv[0]);
+  return CMD_SUCCESS;
+}
+
+void
+install_trill_vlan_elements (void)
+{
+  install_element (INTERFACE_NODE, &trill_isis_vlan_cmd);
+  install_element (INTERFACE_NODE, &trill_isis_no_vlan_cmd);
+  install_element (INTERFACE_NODE, &trill_isis_pvid_cmd);
+}
diff --git isisd/isis_vlans.h isisd/isis_vlans.h
new file mode 100644
index 0000000..9a54e58
--- /dev/null
+++ isisd/isis_vlans.h
@@ -0,0 +1,128 @@
+/*
+ * IS-IS Rout(e)ing protocol - isis_vlans.h
+ *
+ * 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 
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,but WITHOUT 
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+ * more details.
+
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#ifndef  ISIS_VLANS_H
+#define	ISIS_VLANS_H
+
+/* TRILL IS-IS VLANs */
+#define NO_TCI 0
+#define DFLT_VLAN 1
+#define VLANS_SIZE (1<<12)
+#define VLANS_ARRSIZE (VLANS_SIZE/NBBY)
+#define VLAN_MAX 4094
+#define VLAN_MIN 1
+#define VLANTCI(p) ((p)&((VLANS_SIZE)-1))
+
+#define VLANBIT(v) ((v) % NBBY)
+#define VLANBYTE(v) ((v)/NBBY)
+#define CHECK_VLAN(p, v) ((p)[VLANBYTE(v)] & (1<<VLANBIT(v)))
+#define SET_VLAN(p, v) \
+       	do { \
+          if ((v) > 0) \
+       	    ((p)[VLANBYTE(v)] |= 1<<VLANBIT(v)); \
+	} while (0)
+#define CLEAR_VLAN(p, v) \
+	do { \
+          if ((v) > 0) \
+	    ((p)[VLANBYTE(v)] &= ~(1<<VLANBIT(v))); \
+	} while (0)
+
+#define EACH_VLAN(p, v, c) \
+	for ( \
+	(v) = VLAN_MIN; \
+        ((v) <= VLAN_MAX) && \
+	((c) = CHECK_VLAN((p), (v)), 1); \
+	(v)++ )
+
+#define EACH_VLAN_SET(p, v, c) \
+	EACH_VLAN(p, v, c) \
+        if (c) \
+
+#define EACH_VLAN_R(p, v, c) \
+	for (; \
+        ((v) <= VLAN_MAX) && \
+	((c) = CHECK_VLAN((p), (v)), 1); \
+	(v)++ ) \
+
+#define EACH_VLAN_SET_R(p, v, c) \
+	EACH_VLAN_R(p, v, c) \
+	if (c) \
+
+#define MERGE_VLANS(p, v, z) \
+	do { \
+	  for ((v) = 0; (v) < VLANS_ARRSIZE; (v)++) \
+	   (p)[(v)] |= (z)[(v)]; \
+	} while (0)
+
+/* source: http://graphics.stanford.edu/~seander/bithacks.html */
+#define REVERSE_BYTE(v) ((uint8_t)((((v) * 0x0802LU & 0x22110LU) | \
+       	((v) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16));
+
+#define MAX_VLANS_SUBTLV_LEN 240 /* 255 - bytes for type, len, start vlan */
+
+/* Encoded enabled VLANs information for Hello PDU PCTLV_ENABLEDVLANS */
+struct trill_enabled_vlans_listnode
+{
+  uint16_t len;
+  struct trill_enabledvlans_subtlv tlvdata;
+};
+
+/*
+ * Entry on the inhibit_vlan list.  This should be rarely used; it corresponds
+ * to receipt of a Hello message from some other node claiming AF status for a
+ * VLAN for which we were appointed.
+ */
+struct trill_inhibit_vlan
+{
+  time_t reenable;
+  u_int16_t vlan;
+};
+
+struct trill_circuit_vlans
+{
+  u_int16_t pvid;	 /* Default/Native VLAN for untagged frames */
+  u_int16_t designated;	 /* Designated VLAN for the circuit */
+  u_int16_t our_designated; /* Our choice for Designated VLAN */
+  u_int8_t enabled[VLANS_ARRSIZE]; /* VLANs we could be the forwarder */
+  u_int8_t forwarder[VLANS_ARRSIZE]; /* VLANs for which we are the forwarder */
+  u_int16_t rx_tci; /* PCP, CFI, VID */
+  u_int16_t tx_tci; /* PCP, CFI, VID */
+  struct list *enabled_vlans; /* List of enabled vlans TLV data */
+  struct list *appvlanfwders; /* Appointed VLAN forwarders */
+  u_int32_t vlanfwdershash;   /* Hash to check change in VLAN forwarders */
+  struct list *inhibit_vlans;	/* VLANs inhibited by foreign AF flags (rare) */
+  time_t inhibit_all;		/* All inhibited by root bridge (common) */
+  struct thread *inhibit_thread;
+};
+
+struct trill_adj_vlans
+{
+  u_int16_t designated;   /* Designated VLAN when adj is DR */
+  u_int8_t forwarder[VLANS_ARRSIZE];  /* VLANs the adj forwards */
+  u_int8_t enabled[VLANS_ARRSIZE]; /* VLANs the adj has enabled */
+  u_int8_t seen[VLANS_ARRSIZE]; /* VLANs we send hellos to adj */
+};
+
+struct trill_vlan_bridge_roots
+{
+    int vlan_start;
+    int vlan_end;
+    int bridge_roots_count;
+    struct ether_addr *bridge_roots;
+};
+
+#endif /* ISIS_VLANS_H */
diff --git isisd/isisd.c isisd/isisd.c
index 7c669fc..3ea6fcd 100644
--- isisd/isisd.c
+++ isisd/isisd.c
@@ -40,7 +40,6 @@
 #include "isisd/isis_common.h"
 #include "isisd/isis_circuit.h"
 #include "isisd/isis_flags.h"
-#include "isisd/isisd.h"
 #include "isisd/isis_dynhn.h"
 #include "isisd/isis_adjacency.h"
 #include "isisd/isis_pdu.h"
@@ -52,6 +51,12 @@
 #include "isisd/isis_route.h"
 #include "isisd/isis_zebra.h"
 #include "isisd/isis_events.h"
+#ifdef HAVE_TRILL
+#include "isisd/isis_vlans.h"
+#include "isisd/isis_trill.h"
+#endif
+#include "isisd/isisd.h"
+#include "isisd/bool.h"
 
 #ifdef TOPOLOGY_GENERATE
 #include "spgrid.h"
@@ -64,14 +69,11 @@ extern struct thread_master *master;
 /*
  * Prototypes.
  */
-void isis_new(unsigned long);
-struct isis_area *isis_area_create(void);
 int isis_area_get(struct vty *, const char *);
 int isis_area_destroy(struct vty *, const char *);
-int area_net_title(struct vty *, const u_char *);
-int area_clear_net_title(struct vty *, const u_char *);
+int area_net_title(struct vty *, const char *);
+int area_clear_net_title(struct vty *, const char *);
 int show_clns_neigh(struct vty *, char);
-void print_debug(struct vty *, int, int);
 int isis_config_write(struct vty *);
 
 
@@ -100,7 +102,7 @@ isis_new (unsigned long process_id)
 }
 
 struct isis_area *
-isis_area_create ()
+isis_area_create (const char *area_tag)
 {
   struct isis_area *area;
 
@@ -131,6 +133,9 @@ isis_area_create ()
   area->area_addrs = list_new ();
   THREAD_TIMER_ON (master, area->t_tick, lsp_tick, area, 1);
   flags_initialize (&area->flags);
+#ifdef HAVE_TRILL
+  area->trill = XCALLOC (MTYPE_ISIS_TRILLAREA, sizeof (struct trill_info));
+#endif
   /*
    * Default values
    */
@@ -152,6 +157,15 @@ isis_area_create ()
   /* FIXME: Think of a better way... */
   area->min_bcast_mtu = 1497;
 
+  area->area_tag = strdup (area_tag);
+  listnode_add (isis->area_list, area);
+  area->isis = isis;
+
+#ifdef HAVE_TRILL
+  if (isis->trill_active)
+    trill_area_init (area);
+#endif
+
   return area;
 }
 
@@ -184,9 +198,17 @@ isis_area_get (struct vty *vty, const char *area_tag)
       return CMD_SUCCESS;
     }
 
-  area = isis_area_create ();
-  area->area_tag = strdup (area_tag);
-  listnode_add (isis->area_list, area);
+#ifdef HAVE_TRILL
+  if (isis->trill_active && isis->area_list->count != 0)
+    { 
+      vty_out (vty,
+	"TRILL area exists, can only define one IS-IS area for TRILL. %s",
+	VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+#endif
+
+  area = isis_area_create (area_tag);
 
   if (isis->debugs & DEBUG_EVENTS)
     zlog_debug ("New IS-IS area instance %s", area->area_tag);
@@ -212,6 +234,12 @@ isis_area_destroy (struct vty *vty, const char *area_tag)
       return CMD_WARNING;
     }
 
+#ifdef HAVE_TRILL
+  trill_area_free (area);
+  XFREE (MTYPE_ISIS_TRILLAREA, area->trill);
+#endif
+  spftree_area_del (area);
+
   if (area->circuit_list)
     {
       for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit))
@@ -230,13 +258,14 @@ isis_area_destroy (struct vty *vty, const char *area_tag)
   THREAD_TIMER_OFF (area->t_lsp_refresh[0]);
   THREAD_TIMER_OFF (area->t_lsp_refresh[1]);
 
+  free (area->area_tag);
   XFREE (MTYPE_ISIS_AREA, area);
 
   return CMD_SUCCESS;
 }
 
 int
-area_net_title (struct vty *vty, const u_char *net_title)
+area_net_title (struct vty *vty, const char *net_title)
 {
   struct isis_area *area;
   struct area_addr *addr;
@@ -330,7 +359,7 @@ area_net_title (struct vty *vty, const u_char *net_title)
 }
 
 int
-area_clear_net_title (struct vty *vty, const u_char *net_title)
+area_clear_net_title (struct vty *vty, const char *net_title)
 {
   struct isis_area *area;
   struct area_addr addr, *addrp = NULL;
@@ -524,7 +553,10 @@ print_debug (struct vty *vty, int flags, int onoff)
 	     VTY_NEWLINE);
   if (flags & DEBUG_EVENTS)
     vty_out (vty, "IS-IS Event debugging is %s%s", onoffs, VTY_NEWLINE);
-
+#ifdef HAVE_TRILL
+  if (flags & DEBUG_TRILL_EVENTS)
+    vty_out (vty, "IS-IS TRILL Event debugging is %s%s", onoffs, VTY_NEWLINE);
+#endif
 }
 
 DEFUN (show_debugging,
@@ -606,6 +638,13 @@ config_write_debug (struct vty *vty)
       vty_out (vty, "debug isis events%s", VTY_NEWLINE);
       write++;
     }
+#ifdef HAVE_TRILL
+  if (flags & DEBUG_TRILL_EVENTS)
+    {
+      vty_out (vty, "debug trill events%s", VTY_NEWLINE);
+      write++;
+    }
+#endif
 
   return write;
 }
@@ -792,7 +831,6 @@ DEFUN (no_debug_isis_spfevents,
   return CMD_SUCCESS;
 }
 
-
 DEFUN (debug_isis_spfstats,
        debug_isis_spfstats_cmd,
        "debug isis spf-statistics ",
@@ -1045,7 +1083,7 @@ DEFUN (area_passwd,
 
   if (!area)
     {
-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
+      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
       return CMD_WARNING;
     }
 
@@ -1098,7 +1136,7 @@ DEFUN (no_area_passwd,
 
   if (!area)
     {
-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
+      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
       return CMD_WARNING;
     }
 
@@ -1120,7 +1158,7 @@ DEFUN (domain_passwd,
 
   if (!area)
     {
-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
+      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
       return CMD_WARNING;
     }
 
@@ -1173,7 +1211,7 @@ DEFUN (no_domain_passwd,
 
   if (!area)
     {
-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
+      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
       return CMD_WARNING;
     }
 
@@ -1197,7 +1235,7 @@ DEFUN (is_type,
 
   if (!area)
     {
-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
+      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
       return CMD_WARNING;
     }
 
@@ -1207,6 +1245,14 @@ DEFUN (is_type,
       vty_out (vty, "Unknown IS level %s", VTY_NEWLINE);
       return CMD_SUCCESS;
     }
+ 
+#ifdef HAVE_TRILL
+  if (area->isis->trill_active)
+    {
+      vty_out (vty, "Can't set level when TRILL is enabled%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+#endif
 
   isis_event_system_type_change (area, type);
 
@@ -1228,6 +1274,14 @@ DEFUN (no_is_type,
   area = vty->index;
   assert (area);
 
+#ifdef HAVE_TRILL
+  if (area->isis->trill_active)
+    {
+      vty_out (vty, "Can't reset level when TRILL is enabled%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+#endif
+
   /*
    * Put the is-type back to default. Which is level-1-2 on first
    * circuit for the area level-1 for the rest
@@ -1894,6 +1948,15 @@ isis_config_write (struct vty *vty)
       struct isis_area *area;
       struct listnode *node, *node2;
 
+#ifdef HAVE_TRILL
+      /* ISIS - TRILL enabled control flag */
+      if (isis->trill_active)
+        {
+          vty_out (vty, "isis trill%s", VTY_NEWLINE);
+          write++;
+	}
+#endif
+
       for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
       {
 	/* ISIS - Area name */
@@ -1943,6 +2006,31 @@ isis_config_write (struct vty *vty)
 		write++;
 	      }
 	  }
+
+#ifdef HAVE_TRILL
+	/* ISIS TRILL configuration options */
+	if (isis->trill_active)
+          {
+	     if (CHECK_FLAG(area->trill->status, TRILL_NICK_SET) &&
+	          (!CHECK_FLAG(area->trill->status, TRILL_AUTONICK)))
+               {
+                 vty_out (vty, " trill nickname %d%s", area->trill->nick.name,
+				 VTY_NEWLINE);
+                 write++;
+               }
+	     if (CHECK_FLAG(area->trill->status, TRILL_PRIORITY_SET))
+               {
+                 vty_out (vty, " trill nickname priority %d%s",
+			 (area->trill->nick.priority & (~CONFIGURED_NICK_PRIORITY)),
+			 VTY_NEWLINE);
+                 write++;
+               }
+
+	     vty_out(vty, " trill instance %s%s", area->trill->name, VTY_NEWLINE);
+	     write++;
+	  }
+#endif
+
 	/* ISIS - Lsp generation interval */
 	if (area->lsp_gen_interval[0] == area->lsp_gen_interval[1])
 	  {
@@ -2053,7 +2141,7 @@ isis_config_write (struct vty *vty)
 		    ISIS_SYS_ID_LEN))
 	  {
 	    vty_out (vty, " topology base-is %s%s",
-		     sysid_print (area->topology_baseis), VTY_NEWLINE);
+		     sysid_print ((u_char *)area->topology_baseis), VTY_NEWLINE);
 	    write++;
 	  }
 	if (area->topology_basedynh)
@@ -2218,9 +2306,4 @@ isis_init ()
   install_element (VIEW_NODE, &show_isis_generated_topology_cmd);
   install_element (ENABLE_NODE, &show_isis_generated_topology_cmd);
 #endif /* TOPOLOGY_GENERATE */
-
-  isis_new (0);
-  isis_circuit_init ();
-  isis_zebra_init ();
-  isis_spf_cmds_init ();
 }
diff --git isisd/isisd.h isisd/isisd.h
index 2277f27..945d423 100644
--- isisd/isisd.h
+++ isisd/isisd.h
@@ -51,6 +51,9 @@ struct isis
   u_int32_t debugs;		/* bitmap for debug */
   time_t uptime;		/* when did we start */
   struct thread *t_dync_clean;	/* dynamic hostname cache cleanup thread */
+#ifdef HAVE_TRILL
+  int trill_active;		/* TRILL support is active */
+#endif
 
   /* Redistributed external information. */
   struct route_table *external_info[ZEBRA_ROUTE_MAX + 1];
@@ -78,6 +81,8 @@ struct isis
 #endif
 };
 
+extern struct isis *isis;
+
 struct isis_area
 {
   struct isis *isis;				  /* back pointer */
@@ -95,6 +100,9 @@ struct isis_area
   struct thread *t_remove_aged;
   int lsp_regenerate_pending[ISIS_LEVELS];
   struct thread *t_lsp_refresh[ISIS_LEVELS];
+#ifdef HAVE_TRILL
+  struct trill_info *trill;     /* TRILL IS-IS information */
+#endif
 
   /*
    * Configurables 
@@ -128,14 +136,20 @@ struct isis_area
 
 #ifdef TOPOLOGY_GENERATE
   struct list *topology;
-  char topology_baseis[ISIS_SYS_ID_LEN];  /* IS for the first IS emulated. */
+  u_char topology_baseis[ISIS_SYS_ID_LEN];  /* IS for the first IS emulated. */
   char *topology_basedynh;                /* Dynamic hostname base. */
   char top_params[200];                   /* FIXME: what is reasonable? */
 #endif /* TOPOLOGY_GENERATE */
 };
 
 void isis_init (void);
+void isis_new(unsigned long);
+struct isis_area *isis_area_create(const char *);
 struct isis_area *isis_area_lookup (const char *);
+void print_debug(struct vty *, int, int);
+
+/* Master of threads. */
+extern struct thread_master *master;
 
 #define DEBUG_ADJ_PACKETS                (1<<0)
 #define DEBUG_CHECKSUM_ERRORS            (1<<1)
@@ -149,5 +163,8 @@ struct isis_area *isis_area_lookup (const char *);
 #define DEBUG_RTE_EVENTS                 (1<<9)
 #define DEBUG_EVENTS                     (1<<10)
 #define DEBUG_ZEBRA                      (1<<11)
+#ifdef HAVE_TRILL
+#define DEBUG_TRILL_EVENTS		 (1<<12)
+#endif
 
 #endif /* ISISD_H */
diff --git lib/command.h lib/command.h
index a725378..fefa6d3 100644
--- lib/command.h
+++ lib/command.h
@@ -303,6 +303,8 @@
 #define OSPF6_DUMP_TYPE_LIST \
 "(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)"
 #define ISIS_STR "IS-IS information\n"
+#define TRILL_STR "TRILL IS-IS information\n"
+#define TRILL_NICK_STR "Configure TRILL RBridge nickname information\n"
 #define AREA_TAG_STR "[area tag]\n"
 
 #define CONF_BACKUP_EXT ".sav"
diff --git lib/memtypes.c lib/memtypes.c
index c7028c9..476ad35 100644
--- lib/memtypes.c
+++ lib/memtypes.c
@@ -228,20 +228,32 @@
 
 struct memory_list memory_list_isis[] =
 {
-  { MTYPE_ISIS,               "ISIS"				},
-  { MTYPE_ISIS_TMP,           "ISIS TMP"			},
-  { MTYPE_ISIS_CIRCUIT,       "ISIS circuit"			},
-  { MTYPE_ISIS_LSP,           "ISIS LSP"			},
-  { MTYPE_ISIS_ADJACENCY,     "ISIS adjacency"			},
-  { MTYPE_ISIS_AREA,          "ISIS area"			},
-  { MTYPE_ISIS_AREA_ADDR,     "ISIS area address"		},
-  { MTYPE_ISIS_TLV,           "ISIS TLV"			},
-  { MTYPE_ISIS_DYNHN,         "ISIS dyn hostname"		},
-  { MTYPE_ISIS_SPFTREE,       "ISIS SPFtree"			},
-  { MTYPE_ISIS_VERTEX,        "ISIS vertex"			},
-  { MTYPE_ISIS_ROUTE_INFO,    "ISIS route info"			},
-  { MTYPE_ISIS_NEXTHOP,       "ISIS nexthop"			},
-  { MTYPE_ISIS_NEXTHOP6,      "ISIS nexthop6"			},
+  { MTYPE_ISIS,                  "ISIS"				},
+  { MTYPE_ISIS_TMP,              "ISIS TMP"			},
+  { MTYPE_ISIS_CIRCUIT,          "ISIS circuit"			},
+  { MTYPE_ISIS_LSP,              "ISIS LSP"			},
+  { MTYPE_ISIS_ADJACENCY,        "ISIS adjacency"		},
+  { MTYPE_ISIS_AREA,             "ISIS area"			},
+  { MTYPE_ISIS_AREA_ADDR,        "ISIS area address"		},
+  { MTYPE_ISIS_TLV,              "ISIS TLV"			},
+  { MTYPE_ISIS_DYNHN,            "ISIS dyn hostname"		},
+  { MTYPE_ISIS_SPFTREE,          "ISIS SPFtree"			},
+  { MTYPE_ISIS_VERTEX,           "ISIS vertex"			},
+  { MTYPE_ISIS_ROUTE_INFO,       "ISIS route info"		},
+  { MTYPE_ISIS_NEXTHOP,          "ISIS nexthop"			},
+  { MTYPE_ISIS_NEXTHOP6,         "ISIS nexthop6"		},
+  { MTYPE_ISIS_TRILLAREA,	 "ISIS TRILL area information"  },
+  { MTYPE_ISIS_TRILL_NICKDB_NODE,"ISIS TRILL nickdb node"   	},
+  { MTYPE_ISIS_TRILL_FWDTBL_NODE,"ISIS TRILL forwarding table node"},
+  { MTYPE_ISIS_TRILL_VLANS,	 "ISIS TRILL vlans"},
+  { MTYPE_ISIS_TRILL_ADJVLANS,	 "ISIS TRILL adjacency vlans"},
+  { MTYPE_ISIS_TRILL_VLANBRIDGE_ROOTS, "ISIS TRILL vlan bridge roots"},
+  { MTYPE_ISIS_TRILL_BRIDGE_ROOTIDS, "ISIS TRILL vlan bridge root IDs"},
+  { MTYPE_ISIS_TRILL_VLANSREACHABLE, "ISIS TRILL vlans reachable downstream"},
+  { MTYPE_ISIS_TRILL_VLANFWDERS, "ISIS TRILL VLAN forwarders" },
+  { MTYPE_ISIS_TRILL_ENABLEDVLANS, "ISIS TRILL enabled VLAN maps" },
+  { MTYPE_ISIS_TRILL_VLANSUBTLV, "ISIS TRILL VLAN subTLV construction" },
+  { MTYPE_ISIS_TRILL_INHIB,	 "ISIS TRILL VLAN inhibit entry" },
   { -1, NULL },
 };
 
diff --git lib/memtypes.h /dev/null
deleted file mode 100644
index 2d90e80..0000000
--- lib/memtypes.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/* Auto-generated from memtypes.c by gawk. */
-/* Do not edit! */
-
-#ifndef _QUAGGA_MEMTYPES_H
-#define _QUAGGA_MEMTYPES_H
-
-enum
-{
-  MTYPE_TMP = 1,
-  MTYPE_STRVEC,
-  MTYPE_VECTOR,
-  MTYPE_VECTOR_INDEX,
-  MTYPE_LINK_LIST,
-  MTYPE_LINK_NODE,
-  MTYPE_THREAD,
-  MTYPE_THREAD_MASTER,
-  MTYPE_THREAD_STATS,
-  MTYPE_THREAD_FUNCNAME,
-  MTYPE_VTY,
-  MTYPE_VTY_OUT_BUF,
-  MTYPE_VTY_HIST,
-  MTYPE_IF,
-  MTYPE_CONNECTED,
-  MTYPE_CONNECTED_LABEL,
-  MTYPE_BUFFER,
-  MTYPE_BUFFER_DATA,
-  MTYPE_STREAM,
-  MTYPE_STREAM_DATA,
-  MTYPE_STREAM_FIFO,
-  MTYPE_PREFIX,
-  MTYPE_PREFIX_IPV4,
-  MTYPE_PREFIX_IPV6,
-  MTYPE_HASH,
-  MTYPE_HASH_BACKET,
-  MTYPE_HASH_INDEX,
-  MTYPE_ROUTE_TABLE,
-  MTYPE_ROUTE_NODE,
-  MTYPE_DISTRIBUTE,
-  MTYPE_DISTRIBUTE_IFNAME,
-  MTYPE_ACCESS_LIST,
-  MTYPE_ACCESS_LIST_STR,
-  MTYPE_ACCESS_FILTER,
-  MTYPE_PREFIX_LIST,
-  MTYPE_PREFIX_LIST_ENTRY,
-  MTYPE_PREFIX_LIST_STR,
-  MTYPE_ROUTE_MAP,
-  MTYPE_ROUTE_MAP_NAME,
-  MTYPE_ROUTE_MAP_INDEX,
-  MTYPE_ROUTE_MAP_RULE,
-  MTYPE_ROUTE_MAP_RULE_STR,
-  MTYPE_ROUTE_MAP_COMPILED,
-  MTYPE_DESC,
-  MTYPE_KEY,
-  MTYPE_KEYCHAIN,
-  MTYPE_IF_RMAP,
-  MTYPE_IF_RMAP_NAME,
-  MTYPE_SOCKUNION,
-  MTYPE_PRIVS,
-  MTYPE_ZLOG,
-  MTYPE_ZCLIENT,
-  MTYPE_WORK_QUEUE,
-  MTYPE_WORK_QUEUE_ITEM,
-  MTYPE_WORK_QUEUE_NAME,
-  MTYPE_PQUEUE,
-  MTYPE_PQUEUE_DATA,
-  MTYPE_HOST,
-  MTYPE_RTADV_PREFIX,
-  MTYPE_VRF,
-  MTYPE_VRF_NAME,
-  MTYPE_NEXTHOP,
-  MTYPE_RIB,
-  MTYPE_RIB_QUEUE,
-  MTYPE_STATIC_IPV4,
-  MTYPE_STATIC_IPV6,
-  MTYPE_BGP,
-  MTYPE_BGP_LISTENER,
-  MTYPE_BGP_PEER,
-  MTYPE_BGP_PEER_HOST,
-  MTYPE_PEER_GROUP,
-  MTYPE_PEER_DESC,
-  MTYPE_PEER_PASSWORD,
-  MTYPE_ATTR,
-  MTYPE_ATTR_EXTRA,
-  MTYPE_AS_PATH,
-  MTYPE_AS_SEG,
-  MTYPE_AS_SEG_DATA,
-  MTYPE_AS_STR,
-  MTYPE_BGP_TABLE,
-  MTYPE_BGP_NODE,
-  MTYPE_BGP_ROUTE,
-  MTYPE_BGP_ROUTE_EXTRA,
-  MTYPE_BGP_CONN,
-  MTYPE_BGP_STATIC,
-  MTYPE_BGP_ADVERTISE_ATTR,
-  MTYPE_BGP_ADVERTISE,
-  MTYPE_BGP_SYNCHRONISE,
-  MTYPE_BGP_ADJ_IN,
-  MTYPE_BGP_ADJ_OUT,
-  MTYPE_AS_LIST,
-  MTYPE_AS_FILTER,
-  MTYPE_AS_FILTER_STR,
-  MTYPE_COMMUNITY,
-  MTYPE_COMMUNITY_VAL,
-  MTYPE_COMMUNITY_STR,
-  MTYPE_ECOMMUNITY,
-  MTYPE_ECOMMUNITY_VAL,
-  MTYPE_ECOMMUNITY_STR,
-  MTYPE_COMMUNITY_LIST,
-  MTYPE_COMMUNITY_LIST_NAME,
-  MTYPE_COMMUNITY_LIST_ENTRY,
-  MTYPE_COMMUNITY_LIST_CONFIG,
-  MTYPE_COMMUNITY_LIST_HANDLER,
-  MTYPE_CLUSTER,
-  MTYPE_CLUSTER_VAL,
-  MTYPE_BGP_PROCESS_QUEUE,
-  MTYPE_BGP_CLEAR_NODE_QUEUE,
-  MTYPE_TRANSIT,
-  MTYPE_TRANSIT_VAL,
-  MTYPE_BGP_DISTANCE,
-  MTYPE_BGP_NEXTHOP_CACHE,
-  MTYPE_BGP_CONFED_LIST,
-  MTYPE_PEER_UPDATE_SOURCE,
-  MTYPE_BGP_DAMP_INFO,
-  MTYPE_BGP_DAMP_ARRAY,
-  MTYPE_BGP_REGEXP,
-  MTYPE_BGP_AGGREGATE,
-  MTYPE_RIP,
-  MTYPE_RIP_INFO,
-  MTYPE_RIP_INTERFACE,
-  MTYPE_RIP_PEER,
-  MTYPE_RIP_OFFSET_LIST,
-  MTYPE_RIP_DISTANCE,
-  MTYPE_RIPNG,
-  MTYPE_RIPNG_ROUTE,
-  MTYPE_RIPNG_AGGREGATE,
-  MTYPE_RIPNG_PEER,
-  MTYPE_RIPNG_OFFSET_LIST,
-  MTYPE_RIPNG_RTE_DATA,
-  MTYPE_OSPF_TOP,
-  MTYPE_OSPF_AREA,
-  MTYPE_OSPF_AREA_RANGE,
-  MTYPE_OSPF_NETWORK,
-  MTYPE_OSPF_NEIGHBOR_STATIC,
-  MTYPE_OSPF_IF,
-  MTYPE_OSPF_NEIGHBOR,
-  MTYPE_OSPF_ROUTE,
-  MTYPE_OSPF_TMP,
-  MTYPE_OSPF_LSA,
-  MTYPE_OSPF_LSA_DATA,
-  MTYPE_OSPF_LSDB,
-  MTYPE_OSPF_PACKET,
-  MTYPE_OSPF_FIFO,
-  MTYPE_OSPF_VERTEX,
-  MTYPE_OSPF_VERTEX_PARENT,
-  MTYPE_OSPF_NEXTHOP,
-  MTYPE_OSPF_PATH,
-  MTYPE_OSPF_VL_DATA,
-  MTYPE_OSPF_CRYPT_KEY,
-  MTYPE_OSPF_EXTERNAL_INFO,
-  MTYPE_OSPF_DISTANCE,
-  MTYPE_OSPF_IF_INFO,
-  MTYPE_OSPF_IF_PARAMS,
-  MTYPE_OSPF_MESSAGE,
-  MTYPE_OSPF6_TOP,
-  MTYPE_OSPF6_AREA,
-  MTYPE_OSPF6_IF,
-  MTYPE_OSPF6_NEIGHBOR,
-  MTYPE_OSPF6_ROUTE,
-  MTYPE_OSPF6_PREFIX,
-  MTYPE_OSPF6_MESSAGE,
-  MTYPE_OSPF6_LSA,
-  MTYPE_OSPF6_LSA_SUMMARY,
-  MTYPE_OSPF6_LSDB,
-  MTYPE_OSPF6_VERTEX,
-  MTYPE_OSPF6_SPFTREE,
-  MTYPE_OSPF6_NEXTHOP,
-  MTYPE_OSPF6_EXTERNAL_INFO,
-  MTYPE_OSPF6_OTHER,
-  MTYPE_ISIS,
-  MTYPE_ISIS_TMP,
-  MTYPE_ISIS_CIRCUIT,
-  MTYPE_ISIS_LSP,
-  MTYPE_ISIS_ADJACENCY,
-  MTYPE_ISIS_AREA,
-  MTYPE_ISIS_AREA_ADDR,
-  MTYPE_ISIS_TLV,
-  MTYPE_ISIS_DYNHN,
-  MTYPE_ISIS_SPFTREE,
-  MTYPE_ISIS_VERTEX,
-  MTYPE_ISIS_ROUTE_INFO,
-  MTYPE_ISIS_NEXTHOP,
-  MTYPE_ISIS_NEXTHOP6,
-  MTYPE_VTYSH_CONFIG,
-  MTYPE_VTYSH_CONFIG_LINE,
-  MTYPE_MAX,
-};
-
-extern struct memory_list memory_list_lib[];
-extern struct memory_list memory_list_zebra[];
-extern struct memory_list memory_list_bgp[];
-extern struct memory_list memory_list_rip[];
-extern struct memory_list memory_list_ripng[];
-extern struct memory_list memory_list_ospf[];
-extern struct memory_list memory_list_ospf6[];
-extern struct memory_list memory_list_isis[];
-extern struct memory_list memory_list_vtysh[];
-
-#endif /* _QUAGGA_MEMTYPES_H */
-
diff --git lib/privs.c lib/privs.c
index 69606f5..d290a59 100644
--- lib/privs.c
+++ lib/privs.c
@@ -105,6 +105,7 @@ static struct
   [ZCAP_BIND] =		{ 2, (pvalue_t []) { CAP_NET_BIND_SERVICE,
                                              CAP_NET_BROADCAST 		}, },
   [ZCAP_NET_ADMIN] =	{ 1, (pvalue_t []) { CAP_NET_ADMIN		}, },
+  [ZCAP_DL_CONFIG] =	{ 1, (pvalue_t []) { CAP_NET_ADMIN		}, },
   [ZCAP_NET_RAW] = 	{ 1, (pvalue_t []) { CAP_NET_RAW		}, },
   [ZCAP_CHROOT] = 	{ 1, (pvalue_t []) { CAP_SYS_CHROOT,		}, },
   [ZCAP_NICE] = 	{ 1, (pvalue_t []) { CAP_SYS_NICE 		}, },
@@ -123,6 +124,7 @@ static struct
 #else
   [ZCAP_NET_ADMIN] =	{ 1, (pvalue_t []) { PRIV_SYS_NET_CONFIG	}, },
 #endif
+  [ZCAP_DL_CONFIG] =	{ 1, (pvalue_t []) { PRIV_SYS_DL_CONFIG		}, },
   [ZCAP_NET_RAW] = 	{ 2, (pvalue_t []) { PRIV_NET_RAWACCESS,
                                              PRIV_NET_ICMPACCESS 	}, },
   [ZCAP_CHROOT] = 	{ 1, (pvalue_t []) { PRIV_PROC_CHROOT		}, },
@@ -137,6 +139,7 @@ static struct
                                              PRIV_FILE_DAC_READ		}, },
   [ZCAP_SYS_ADMIN] =	{ 1, (pvalue_t []) { PRIV_SYS_ADMIN		}, },
   [ZCAP_FOWNER] =	{ 1, (pvalue_t []) { PRIV_FILE_OWNER		}, },
+  [ZCAP_EXEC] =		{ 1, (pvalue_t []) { PRIV_PROC_EXEC		}, },
 #endif /* HAVE_SOLARIS_CAPABILITIES */
 };
 
diff --git lib/privs.h lib/privs.h
index 46d614e..13dbfaf 100644
--- lib/privs.h
+++ lib/privs.h
@@ -38,6 +38,8 @@ typedef enum
   ZCAP_DAC_OVERRIDE,
   ZCAP_READ_SEARCH,
   ZCAP_FOWNER,
+  ZCAP_DL_CONFIG,
+  ZCAP_EXEC,
   ZCAP_MAX
 } zebra_capabilities_t;
 
diff --git lib/sigevent.c lib/sigevent.c
index 30e9a3d..2aeb201 100644
--- lib/sigevent.c
+++ lib/sigevent.c
@@ -219,6 +219,7 @@ static void
 trap_default_signals(void)
 {
   static const int core_signals[] = {
+#ifndef NO_CORE_SIGNALS
     SIGQUIT,
     SIGILL,
 #ifdef SIGEMT
@@ -236,6 +237,7 @@ trap_default_signals(void)
 #ifdef SIGXFSZ
     SIGXFSZ,
 #endif
+#endif /* NO_CORE_SIGNALS */
   };
   static const int exit_signals[] = {
     SIGHUP,
diff --git solaris/Makefile.am solaris/Makefile.am
index acccbdb..3520bc5 100644
--- solaris/Makefile.am
+++ solaris/Makefile.am
@@ -14,7 +14,7 @@
 pkg_packages = $(pkg_names:%=@PACKAGE_TARNAME@-%-$(pkg_name_rev).pkg)
 pkg_pkginfos = $(pkg_names:%=pkginfo.%.full)
 pkg_prototypes = $(pkg_names:%=prototype.%)
-pkg_manifests = quagga.xml
+pkg_manifests = quagga.xml trill.xml
 
 # pkgmk variable substitutions wont grok ${variable} in prototype
 # file, so we cant let autoconf generate the file sadly
diff --git solaris/trill.xml.in solaris/trill.xml.in
new file mode 100644
index 0000000..f768878
--- /dev/null
+++ solaris/trill.xml.in
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+	This file is part of Quagga.
+
+	Quagga is free software; you can redistribute it and/or modify it
+	under the terms of the GNU General Public License as published by the
+	Free Software Foundation; either version 2, or (at your option) any
+	later version.
+
+	Quagga is distributed in the hope that it will be useful, but 
+	WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+	General Public License for more details.
+
+	You should have received a copy of the GNU General Public License  
+	along with Quagga; see the file COPYING.  If not, write to the Free
+	Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+	02111-1307, USA.
+
+	Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+	Use is subject to license terms.
+-->
+
+<service_bundle type='manifest' name='SUNWtrill-daemons:quagga'>
+
+<service
+	name='network/routing/trill'
+	type='service'
+	version='1'>
+
+	<dependency name='filesystem'
+		grouping='require_all'
+		restart_on='none'
+		type='service'>
+		<service_fmri value='svc:/system/filesystem/local' />
+	</dependency>
+
+	<dependency name='datalink'
+		grouping='require_all'
+		restart_on='none'
+		type='service'>
+		<service_fmri value='svc:/network/datalink-management' />
+	</dependency>
+
+	<exec_method
+		type='method'
+		name='stop'
+		exec=':kill'
+		timeout_seconds='60'>
+	</exec_method>
+
+	<exec_method
+		type='method'
+		name='start'
+		exec='/usr/sbin/trilld -i "" -P 0 %i &amp;'
+		timeout_seconds='60'>
+		<method_context>
+		  <method_credential
+		   user='root' group='root' />
+ 		</method_context>
+	</exec_method>
+
+	<exec_method
+		type='method'
+		name='refresh'
+		exec=':kill -HUP'
+		timeout_seconds='60'>
+	</exec_method>
+
+	<property_group name='startd'
+		type='framework'>
+		<!-- sub-process core dumps shouldn't restart session -->
+		<propval name='ignore_error'
+		    type='astring' value='core,signal' />
+	</property_group>
+
+	<stability value='Unstable' />
+
+	<template>
+		<common_name>
+			<loctext xml:lang='C'>
+			Quagga: trilld, TRILL bridging protocol daemon.
+			</loctext>
+		</common_name>
+		<documentation>
+			<manpage title='trilld' section='1M'
+				manpath='/usr/share/man' />
+			<doc_link name='quagga.net' 
+				uri='http://www.quagga.net/' />
+		</documentation>
+	</template>
+</service>
+</service_bundle>