--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/25-isisd-dlpi.patch Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,2333 @@
+diff --git a/ChangeLog b/ChangeLog
+index 38ea66d..ad1c40c 100644
+--- ChangeLog
++++ ChangeLog
+@@ -1,3 +1,8 @@
++2007-08-07 James Carlson <[email protected]>
++
++ * configure.ac: Added support for separate link-layer access
++ mechanisms in isisd.
++
+ 2007-07-27 Paul Jakma <[email protected]>
+
+ * configure.ac: Bump version to 0.99.8
+diff --git a/configure.ac b/configure.ac
+index 857f415..437767e 100755
+--- configure.ac
++++ configure.ac
+@@ -750,6 +750,35 @@
+ AC_SUBST(KERNEL_METHOD)
+ AC_SUBST(OTHER_METHOD)
+
++dnl --------------------------
++dnl Determine IS-IS I/O method
++dnl --------------------------
++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=isis_pfpacket.o
++elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
++ AC_MSG_RESULT(DLPI)
++ ISIS_METHOD="isis_dlpi.o"
++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
++ ISIS_METHOD="isis_dlpi.o"
++ else
++ AC_MSG_RESULT(BPF)
++ ISIS_METHOD="isis_bpf.o"
++ fi
++fi
++AC_SUBST(ISIS_METHOD)
++
+ dnl ------------------------------------
+ dnl check for broken CMSG_FIRSTHDR macro
+ dnl ------------------------------------
+diff --git a/isisd/ChangeLog b/isisd/ChangeLog
+index 8797af1..c2482f0 100644
+--- isisd/ChangeLog
++++ isisd/ChangeLog
+@@ -1,3 +1,20 @@
++2008-01-29 James Carlson <[email protected]>
++
++ * Fix bug #437, assert due to bogus index management
++ * isis_flags.c: (flags_initialize) new
++ * (flags_get_index) fix off by one, leading to list assert
++ on null node data.
++ (flags_free_index) ditto.
++ * isisd.c: (isis_area_create) use flags_initialize
++ (isis_area_destroy) deconfigure circuits when
++ taking down area.
++
++2007-07-18 James Carlson <[email protected]>
++
++ * isis_network.c: split up into isis_bpf.c, isis_dlpi.c, and
++ isis_pfpacket.c, selected by autoconf, and added DLPI support.
++ * (general) Fixed to allow compilation and use on Solaris.
++
+ 2006-12-08 Hannes Gredler <[email protected]>
+
+ * isis_adjacency.c: (isis_new_adj) Allow NULL snpa argument.
+diff --git a/isisd/Makefile.am b/isisd/Makefile.am
+index 1dd5493..859facd 100644
+--- isisd/Makefile.am
++++ isisd/Makefile.am
+@@ -9,9 +9,11 @@ noinst_LIBRARIES = libisis.a
+ sbin_PROGRAMS = isisd
+ SUBDIRS = topology
+
++isis_method = @ISIS_METHOD@
++
+ libisis_a_SOURCES = \
+ isis_adjacency.c isis_lsp.c dict.c isis_circuit.c isis_pdu.c \
+- isis_tlv.c isisd.c isis_misc.c isis_network.c isis_zebra.c isis_dr.c \
++ isis_tlv.c isisd.c isis_misc.c isis_zebra.c isis_dr.c \
+ isis_flags.c isis_dynhn.c iso_checksum.c isis_csm.c isis_events.c \
+ isis_spf.c isis_route.c isis_routemap.c
+
+@@ -26,7 +28,11 @@ noinst_HEADERS = \
+ isisd_SOURCES = \
+ isis_main.c $(libisis_a_SOURCES)
+
+-isisd_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
++isisd_LDADD = $(isis_method) @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
++
++isisd_DEPENDENCIES = $(isis_method)
++
++EXTRA_DIST = isis_bpf.c isis_dlpi.c isis_pfpacket.c
+
+ examplesdir = $(exampledir)
+ dist_examples_DATA = isisd.conf.sample
+diff --git a/isisd/dict.c b/isisd/dict.c
+index a333d3e..6c3e1e7 100644
+--- isisd/dict.c
++++ isisd/dict.c
+@@ -14,12 +14,13 @@
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+- * $Id: dict.c,v 1.4 2005/09/25 12:04:25 hasso Exp $
+- * $Name: $
++ * $Id$
++ * $Name$
+ */
+
+ #include <stdlib.h>
+ #include <stddef.h>
++#include "zebra.h"
+ #include "zassert.h"
+ #define DICT_IMPLEMENTATION
+ #include "dict.h"
+diff --git a/isisd/include-netbsd/iso.h b/isisd/include-netbsd/iso.h
+index 714b42d..1a80aec 100644
+--- isisd/include-netbsd/iso.h
++++ isisd/include-netbsd/iso.h
+@@ -192,7 +192,13 @@ extern struct protosw isosw[];
+ #else
+ /* user utilities definitions from the iso library */
+
++#ifdef SUNOS_5
++#define __P(x) x
++#define __BEGIN_DECLS
++#define __END_DECLS
++#else
+ #include <sys/cdefs.h>
++#endif
+
+ __BEGIN_DECLS
+ struct iso_addr *iso_addr __P((const char *));
+diff --git a/isisd/isisd.c b/isisd/isisd.c
+index 714b42d..1a80aec 100644
+--- isisd/isisd.c
++++ isisd/isisd.c
+@@ -130,7 +130,7 @@
+ area->circuit_list = list_new ();
+ area->area_addrs = list_new ();
+ THREAD_TIMER_ON (master, area->t_tick, lsp_tick, area, 1);
+- area->flags.maxindex = -1;
++ flags_initialize (&area->flags);
+ /*
+ * Default values
+ */
+@@ -215,7 +215,11 @@
+ if (area->circuit_list)
+ {
+ for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit))
+- isis_circuit_del (circuit);
++ {
++ /* The fact that it's in circuit_list means that it was configured */
++ isis_circuit_deconfigure (circuit, area);
++ isis_circuit_del (circuit);
++ }
+
+ list_delete (area->circuit_list);
+ }
+diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c
+new file mode 100644
+index 0000000..e66ac98
+--- /dev/null
++++ isisd/isis_bpf.c
+@@ -0,0 +1,340 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_bpf.c
++ *
++ * Copyright (C) 2001,2002 Sampo Saaristo
++ * Tampere University of Technology
++ * Institute of Communications Engineering
++ *
++ * 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.h>
++#include <netinet/if_ether.h>
++#include <sys/time.h>
++#include <sys/ioctl.h>
++#include <net/bpf.h>
++
++#include "log.h"
++#include "stream.h"
++#include "if.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 "privs.h"
++
++extern struct zebra_privs_t isisd_privs;
++
++struct bpf_insn llcfilter[] = {
++ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN), /* check first byte */
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5),
++ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1),
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 3), /* check second byte */
++ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2),
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), /* check third byte */
++ BPF_STMT (BPF_RET + BPF_K, (u_int) - 1),
++ BPF_STMT (BPF_RET + BPF_K, 0)
++};
++int readblen = 0;
++u_char *readbuff = NULL;
++
++/*
++ * Table 9 - Architectural constants for use with ISO 8802 subnetworks
++ * ISO 10589 - 8.4.8
++ */
++
++u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
++u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
++u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
++u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
++
++static char sock_buff[8192];
++
++static int
++open_bpf_dev (struct isis_circuit *circuit)
++{
++ int i = 0, fd;
++ char bpfdev[128];
++ struct ifreq ifr;
++ u_int16_t blen;
++ int true = 1, false = 0;
++ struct timeval timeout;
++ struct bpf_program bpf_prog;
++
++ do
++ {
++ (void) snprintf (bpfdev, sizeof (bpfdev), "/dev/bpf%d", i++);
++ fd = open (bpfdev, O_RDWR);
++ }
++ while (fd < 0 && errno == EBUSY);
++
++ if (fd < 0)
++ {
++ zlog_warn ("open_bpf_dev(): failed to create bpf socket: %s",
++ safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ zlog_debug ("Opened BPF device %s", bpfdev);
++
++ memcpy (ifr.ifr_name, circuit->interface->name, sizeof (ifr.ifr_name));
++ if (ioctl (fd, BIOCSETIF, (caddr_t) & ifr) < 0)
++ {
++ zlog_warn ("open_bpf_dev(): failed to bind to interface: %s",
++ safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ if (ioctl (fd, BIOCGBLEN, (caddr_t) & blen) < 0)
++ {
++ zlog_warn ("failed to get BPF buffer len");
++ blen = circuit->interface->mtu;
++ }
++
++ readblen = blen;
++
++ if (readbuff == NULL)
++ readbuff = malloc (blen);
++
++ zlog_debug ("BPF buffer len = %u", blen);
++
++ /* BPF(4): reads return immediately upon packet reception.
++ * Otherwise, a read will block until either the kernel
++ * buffer becomes full or a timeout occurs.
++ */
++ if (ioctl (fd, BIOCIMMEDIATE, (caddr_t) & true) < 0)
++ {
++ zlog_warn ("failed to set BPF dev to immediate mode");
++ }
++
++#ifdef BIOCSSEESENT
++ /*
++ * We want to see only incoming packets
++ */
++ if (ioctl (fd, BIOCSSEESENT, (caddr_t) & false) < 0)
++ {
++ zlog_warn ("failed to set BPF dev to incoming only mode");
++ }
++#endif
++
++ /*
++ * ...but all of them
++ */
++ if (ioctl (fd, BIOCPROMISC, (caddr_t) & true) < 0)
++ {
++ zlog_warn ("failed to set BPF dev to promiscuous mode");
++ }
++
++ /*
++ * If the buffer length is smaller than our mtu, lets try to increase it
++ */
++ if (blen < circuit->interface->mtu)
++ {
++ if (ioctl (fd, BIOCSBLEN, &circuit->interface->mtu) < 0)
++ {
++ zlog_warn ("failed to set BPF buffer len (%u to %u)", blen,
++ circuit->interface->mtu);
++ }
++ }
++
++ /*
++ * Set a timeout parameter - hope this helps select()
++ */
++ timeout.tv_sec = 600;
++ timeout.tv_usec = 0;
++ if (ioctl (fd, BIOCSRTIMEOUT, (caddr_t) & timeout) < 0)
++ {
++ zlog_warn ("failed to set BPF device timeout");
++ }
++
++ /*
++ * And set the filter
++ */
++ memset (&bpf_prog, 0, sizeof (struct bpf_program));
++ bpf_prog.bf_len = 8;
++ bpf_prog.bf_insns = &(llcfilter[0]);
++ if (ioctl (fd, BIOCSETF, (caddr_t) & bpf_prog) < 0)
++ {
++ zlog_warn ("open_bpf_dev(): failed to install filter: %s",
++ safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ assert (fd > 0);
++
++ 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 = ISIS_OK;
++
++ if (isisd_privs.change (ZPRIVS_RAISE))
++ zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
++
++ retval = open_bpf_dev (circuit);
++
++ if (retval != ISIS_OK)
++ {
++ zlog_warn ("%s: could not initialize the socket", __func__);
++ goto end;
++ }
++
++ if (circuit->circ_type == CIRCUIT_T_BROADCAST)
++ {
++ circuit->tx = isis_send_pdu_bcast;
++ circuit->rx = isis_recv_pdu_bcast;
++ }
++ else if (circuit->circ_type == CIRCUIT_T_P2P)
++ {
++ circuit->tx = isis_send_pdu_p2p;
++ circuit->rx = isis_recv_pdu_p2p;
++ }
++ else
++ {
++ zlog_warn ("isis_sock_init(): unknown circuit type");
++ retval = ISIS_WARNING;
++ goto end;
++ }
++
++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 = 0, bytestoread, offset, one = 1;
++ struct bpf_hdr *bpf_hdr;
++
++ assert (circuit->fd > 0);
++
++ if (ioctl (circuit->fd, FIONREAD, (caddr_t) & bytestoread) < 0)
++ {
++ zlog_warn ("ioctl() FIONREAD failed: %s", safe_strerror (errno));
++ }
++
++ if (bytestoread)
++ {
++ bytesread = read (circuit->fd, readbuff, readblen);
++ }
++ if (bytesread < 0)
++ {
++ zlog_warn ("isis_recv_pdu_bcast(): read() failed: %s",
++ safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ if (bytesread == 0)
++ return ISIS_WARNING;
++
++ bpf_hdr = (struct bpf_hdr *) readbuff;
++
++ assert (bpf_hdr->bh_caplen == bpf_hdr->bh_datalen);
++
++ offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN;
++
++ /* then we lose the BPF, LLC and ethernet headers */
++ stream_write (circuit->rcv_stream, readbuff + offset,
++ bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN);
++ stream_set_getp (circuit->rcv_stream, 0);
++
++ memcpy (ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN,
++ ETHER_ADDR_LEN);
++
++ if (ioctl (circuit->fd, BIOCFLUSH, &one) < 0)
++ zlog_warn ("Flushing failed: %s", safe_strerror (errno));
++
++ return ISIS_OK;
++}
++
++int
++isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
++{
++ int bytesread;
++
++ bytesread = stream_read (circuit->rcv_stream, circuit->fd,
++ circuit->interface->mtu);
++
++ if (bytesread < 0)
++ {
++ zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ return ISIS_OK;
++}
++
++int
++isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
++{
++ struct ether_header *eth;
++ int written;
++
++ stream_set_getp (circuit->snd_stream, 0);
++
++ /*
++ * First the eth header
++ */
++ eth = (struct ether_header *) sock_buff;
++ if (level == 1)
++ memcpy (eth->ether_dhost, ALL_L1_ISS, ETHER_ADDR_LEN);
++ else
++ memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN);
++ memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN);
++ eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
++
++ /*
++ * Then the LLC
++ */
++ sock_buff[ETHER_HDR_LEN] = ISO_SAP;
++ sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP;
++ sock_buff[ETHER_HDR_LEN + 2] = 0x03;
++
++ /* then we copy the data */
++ memcpy (sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data,
++ stream_get_endp (circuit->snd_stream));
++
++ /* now we can send this */
++ written = write (circuit->fd, sock_buff,
++ stream_get_endp (circuit->snd_stream)
++ + LLC_LEN + ETHER_HDR_LEN);
++
++ return ISIS_OK;
++}
++
++int
++isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
++{
++ return ISIS_OK;
++}
+diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
+index fe3eb82..af24988 100644
+--- isisd/isis_circuit.c
++++ isisd/isis_circuit.c
+@@ -26,6 +26,10 @@
+ #include <netinet/if_ether.h>
+ #endif
+
++#ifndef ETHER_ADDR_LEN
++#define ETHER_ADDR_LEN ETHERADDRL
++#endif
++
+ #include "log.h"
+ #include "memory.h"
+ #include "if.h"
+@@ -381,11 +385,13 @@ isis_circuit_if_add (struct isis_circuit *circuit, struct interface *ifp)
+ * Get the Hardware Address
+ */
+ #ifdef HAVE_STRUCT_SOCKADDR_DL
++#ifndef SUNOS_5
+ if (circuit->interface->sdl.sdl_alen != ETHER_ADDR_LEN)
+ zlog_warn ("unsupported link layer");
+ else
+ memcpy (circuit->u.bc.snpa, LLADDR (&circuit->interface->sdl),
+ ETH_ALEN);
++#endif
+ #else
+ if (circuit->interface->hw_addr_len != ETH_ALEN)
+ {
+@@ -447,10 +453,12 @@ isis_circuit_update_params (struct isis_circuit *circuit,
+ * Get the Hardware Address
+ */
+ #ifdef HAVE_STRUCT_SOCKADDR_DL
++#ifndef SUNOS_5
+ if (circuit->interface->sdl.sdl_alen != ETHER_ADDR_LEN)
+ zlog_warn ("unsupported link layer");
+ else
+ memcpy (circuit->u.bc.snpa, LLADDR (&circuit->interface->sdl), ETH_ALEN);
++#endif
+ #else
+ if (circuit->interface->hw_addr_len != ETH_ALEN)
+ {
+diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
+index b5ef269..a7e719f 100644
+--- isisd/isis_circuit.h
++++ isisd/isis_circuit.h
+@@ -69,6 +69,7 @@ struct isis_circuit
+ struct isis_area *area; /* back pointer to the area */
+ struct interface *interface; /* interface info from z */
+ int fd; /* IS-IS l1/2 socket */
++ int sap_length; /* SAP length for DLPI */
+ struct nlpids nlpids;
+ /*
+ * Threads
+diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c
+new file mode 100644
+index 0000000..db4383b
+--- /dev/null
++++ isisd/isis_dlpi.c
+@@ -0,0 +1,607 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_dlpi.c
++ *
++ * Copyright (C) 2001,2002 Sampo Saaristo
++ * Tampere University of Technology
++ * Institute of Communications Engineering
++ *
++ * 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.h>
++#include <netinet/if_ether.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <stropts.h>
++#include <poll.h>
++#include <sys/dlpi.h>
++#include <sys/pfmod.h>
++
++#include "log.h"
++#include "stream.h"
++#include "if.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 "privs.h"
++
++extern struct zebra_privs_t isisd_privs;
++
++static t_uscalar_t dlpi_ctl[1024]; /* DLPI control messages */
++
++/*
++ * Table 9 - Architectural constants for use with ISO 8802 subnetworks
++ * ISO 10589 - 8.4.8
++ */
++
++u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
++u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
++u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
++u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
++
++static u_char sock_buff[8192];
++
++static u_short pf_filter[] =
++{
++ ENF_PUSHWORD + 0, /* Get the SSAP/DSAP values */
++ ENF_PUSHLIT | ENF_CAND, /* Check them */
++ ISO_SAP | (ISO_SAP << 8),
++ ENF_PUSHWORD + 1, /* Get the control value */
++ ENF_PUSHLIT | ENF_AND, /* Isolate it */
++#ifdef _BIG_ENDIAN
++ 0xFF00,
++#else
++ 0x00FF,
++#endif
++ ENF_PUSHLIT | ENF_CAND, /* Test for expected value */
++#ifdef _BIG_ENDIAN
++ 0x0300
++#else
++ 0x0003
++#endif
++};
++
++/*
++ * We would like to use something like libdlpi here, but that's not present on
++ * all versions of Solaris or on any non-Solaris system, so it's nowhere near
++ * as portable as we'd like. Thus, we use the standards-conformant DLPI
++ * interfaces plus the (optional; not needed) Solaris packet filter module.
++ */
++
++static void
++dlpisend (int fd, const void *cbuf, size_t cbuflen,
++ const void *dbuf, size_t dbuflen, int flags)
++{
++ const struct strbuf *ctlptr = NULL;
++ const struct strbuf *dataptr = NULL;
++ struct strbuf ctlbuf, databuf;
++
++ if (cbuf != NULL)
++ {
++ memset (&ctlbuf, 0, sizeof (ctlbuf));
++ ctlbuf.len = cbuflen;
++ ctlbuf.buf = (void *)cbuf;
++ ctlptr = &ctlbuf;
++ }
++
++ if (dbuf != NULL)
++ {
++ memset (&databuf, 0, sizeof (databuf));
++ databuf.len = dbuflen;
++ databuf.buf = (void *)dbuf;
++ dataptr = &databuf;
++ }
++
++ /* We assume this doesn't happen often and isn't operationally significant */
++ if (putmsg (fd, ctlptr, dataptr, flags) == -1)
++ zlog_debug ("%s: putmsg: %s", __func__, safe_strerror (errno));
++}
++
++static ssize_t
++dlpirctl (int fd)
++{
++ struct pollfd fds[1];
++ struct strbuf ctlbuf, databuf;
++ int flags, retv;
++
++ do
++ {
++ /* Poll is used here in case the device doesn't speak DLPI correctly */
++ memset (fds, 0, sizeof (fds));
++ fds[0].fd = fd;
++ fds[0].events = POLLIN | POLLPRI;
++ if (poll (fds, 1, 1000) <= 0)
++ return -1;
++
++ memset (&ctlbuf, 0, sizeof (ctlbuf));
++ memset (&databuf, 0, sizeof (databuf));
++ ctlbuf.maxlen = sizeof (dlpi_ctl);
++ ctlbuf.buf = (void *)dlpi_ctl;
++ databuf.maxlen = sizeof (sock_buff);
++ databuf.buf = (void *)sock_buff;
++ flags = 0;
++ retv = getmsg (fd, &ctlbuf, &databuf, &flags);
++
++ if (retv < 0)
++ return -1;
++ }
++ while (ctlbuf.len == 0);
++
++ if (!(retv & MORECTL))
++ {
++ while (retv & MOREDATA)
++ {
++ flags = 0;
++ retv = getmsg (fd, NULL, &databuf, &flags);
++ }
++ return ctlbuf.len;
++ }
++
++ while (retv & MORECTL)
++ {
++ flags = 0;
++ retv = getmsg (fd, &ctlbuf, &databuf, &flags);
++ }
++ return -1;
++}
++
++static int
++dlpiok (int fd, t_uscalar_t oprim)
++{
++ int retv;
++ dl_ok_ack_t *doa = (dl_ok_ack_t *)dlpi_ctl;
++
++ retv = dlpirctl (fd);
++ if (retv < DL_OK_ACK_SIZE || doa->dl_primitive != DL_OK_ACK ||
++ doa->dl_correct_primitive != oprim)
++ {
++ return -1;
++ }
++ else
++ {
++ return 0;
++ }
++}
++
++static int
++dlpiinfo (int fd)
++{
++ dl_info_req_t dir;
++ ssize_t retv;
++
++ memset (&dir, 0, sizeof (dir));
++ dir.dl_primitive = DL_INFO_REQ;
++ /* Info_req uses M_PCPROTO. */
++ dlpisend (fd, &dir, sizeof (dir), NULL, 0, RS_HIPRI);
++ retv = dlpirctl (fd);
++ if (retv < DL_INFO_ACK_SIZE || dlpi_ctl[0] != DL_INFO_ACK)
++ return -1;
++ else
++ return retv;
++}
++
++static int
++dlpiopen (const char *devpath, ssize_t *acklen)
++{
++ int fd, flags;
++
++ fd = open (devpath, O_RDWR | O_NONBLOCK | O_NOCTTY);
++ if (fd == -1)
++ return -1;
++
++ /* All that we want is for the open itself to be non-blocking, not I/O. */
++ flags = fcntl (fd, F_GETFL, 0);
++ if (flags != -1)
++ fcntl (fd, F_SETFL, flags & ~O_NONBLOCK);
++
++ /* After opening, ask for information */
++ if ((*acklen = dlpiinfo (fd)) == -1)
++ {
++ close (fd);
++ return -1;
++ }
++
++ return fd;
++}
++
++static int
++dlpiattach (int fd, int unit)
++{
++ dl_attach_req_t dar;
++
++ memset (&dar, 0, sizeof (dar));
++ dar.dl_primitive = DL_ATTACH_REQ;
++ dar.dl_ppa = unit;
++ dlpisend (fd, &dar, sizeof (dar), NULL, 0, 0);
++ return dlpiok (fd, dar.dl_primitive);
++}
++
++static int
++dlpibind (int fd)
++{
++ dl_bind_req_t dbr;
++ int retv;
++ dl_bind_ack_t *dba = (dl_bind_ack_t *)dlpi_ctl;
++
++ memset (&dbr, 0, sizeof (dbr));
++ dbr.dl_primitive = DL_BIND_REQ;
++ dbr.dl_service_mode = DL_CLDLS;
++ dlpisend (fd, &dbr, sizeof (dbr), NULL, 0, 0);
++
++ retv = dlpirctl (fd);
++ if (retv < DL_BIND_ACK_SIZE || dba->dl_primitive != DL_BIND_ACK)
++ return -1;
++ else
++ return 0;
++}
++
++static int
++dlpimcast (int fd, const u_char *mcaddr)
++{
++ struct {
++ dl_enabmulti_req_t der;
++ u_char addr[ETHERADDRL];
++ } dler;
++
++ memset (&dler, 0, sizeof (dler));
++ dler.der.dl_primitive = DL_ENABMULTI_REQ;
++ dler.der.dl_addr_length = sizeof (dler.addr);
++ dler.der.dl_addr_offset = dler.addr - (u_char *)&dler;
++ memcpy (dler.addr, mcaddr, sizeof (dler.addr));
++ dlpisend (fd, &dler, sizeof (dler), NULL, 0, 0);
++ return dlpiok (fd, dler.der.dl_primitive);
++}
++
++static int
++dlpiaddr (int fd, u_char *addr)
++{
++ dl_phys_addr_req_t dpar;
++ dl_phys_addr_ack_t *dpaa = (dl_phys_addr_ack_t *)dlpi_ctl;
++ int retv;
++
++ memset (&dpar, 0, sizeof (dpar));
++ dpar.dl_primitive = DL_PHYS_ADDR_REQ;
++ dpar.dl_addr_type = DL_CURR_PHYS_ADDR;
++ dlpisend (fd, &dpar, sizeof (dpar), NULL, 0, 0);
++
++ retv = dlpirctl (fd);
++ if (retv < DL_PHYS_ADDR_ACK_SIZE || dpaa->dl_primitive != DL_PHYS_ADDR_ACK)
++ return -1;
++
++ if (dpaa->dl_addr_offset < DL_PHYS_ADDR_ACK_SIZE ||
++ dpaa->dl_addr_length != ETHERADDRL ||
++ dpaa->dl_addr_offset + dpaa->dl_addr_length > retv)
++ return -1;
++
++ bcopy((char *)dpaa + dpaa->dl_addr_offset, addr, ETHERADDRL);
++ return 0;
++}
++
++static int
++open_dlpi_dev (struct isis_circuit *circuit)
++{
++ int fd, unit, retval;
++ char devpath[MAXPATHLEN];
++ dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl;
++ ssize_t acklen;
++
++ /* Only broadcast-type are supported at the moment */
++ if (circuit->circ_type != CIRCUIT_T_BROADCAST)
++ {
++ zlog_warn ("%s: non-broadcast interface %s", __func__,
++ circuit->interface->name);
++ return ISIS_WARNING;
++ }
++
++ /* Try first as Style 1 */
++ (void) snprintf(devpath, sizeof (devpath), "/dev/%s",
++ circuit->interface->name);
++ unit = -1;
++ fd = dlpiopen (devpath, &acklen);
++
++ /* If that fails, try again as Style 2 */
++ if (fd == -1)
++ {
++ char *cp;
++
++ cp = devpath + strlen (devpath);
++ while (--cp >= devpath && isdigit(*cp))
++ ;
++ unit = strtol(cp, NULL, 0);
++ *cp = '\0';
++ fd = dlpiopen (devpath, &acklen);
++
++ /* If that too fails, then the device really doesn't exist */
++ if (fd == -1)
++ {
++ zlog_warn ("%s: unknown interface %s", __func__,
++ circuit->interface->name);
++ return ISIS_WARNING;
++ }
++
++ /* Double check the DLPI style */
++ if (dia->dl_provider_style != DL_STYLE2)
++ {
++ zlog_warn ("open_dlpi_dev(): interface %s: %s is not style 2",
++ circuit->interface->name, devpath);
++ close (fd);
++ return ISIS_WARNING;
++ }
++
++ /* If it succeeds, then we need to attach to the unit specified */
++ dlpiattach (fd, unit);
++
++ /* Reget the information, as it may be different per node */
++ if ((acklen = dlpiinfo (fd)) == -1)
++ {
++ close (fd);
++ return ISIS_WARNING;
++ }
++ }
++ else
++ {
++ /* Double check the DLPI style */
++ if (dia->dl_provider_style != DL_STYLE1)
++ {
++ zlog_warn ("open_dlpi_dev(): interface %s: %s is not style 1",
++ circuit->interface->name, devpath);
++ close (fd);
++ return ISIS_WARNING;
++ }
++ }
++
++ /* Check that the interface we've got is the kind we expect */
++ if ((dia->dl_sap_length != 2 && dia->dl_sap_length != -2) ||
++ dia->dl_service_mode != DL_CLDLS || dia->dl_addr_length != ETHERADDRL + 2 ||
++ dia->dl_brdcst_addr_length != ETHERADDRL)
++ {
++ zlog_warn ("%s: unsupported interface type for %s", __func__,
++ circuit->interface->name);
++ close (fd);
++ return ISIS_WARNING;
++ }
++ switch (dia->dl_mac_type)
++ {
++ case DL_CSMACD:
++ case DL_ETHER:
++ case DL_100VG:
++ case DL_100VGTPR:
++ case DL_ETH_CSMA:
++ case DL_100BT:
++ break;
++ default:
++ zlog_warn ("%s: unexpected mac type on %s: %d", __func__,
++ circuit->interface->name, dia->dl_mac_type);
++ close (fd);
++ return ISIS_WARNING;
++ }
++
++ circuit->sap_length = dia->dl_sap_length;
++
++ /*
++ * The local hardware address is something that should be provided by way of
++ * sockaddr_dl for the interface, but isn't on Solaris. We set it here based
++ * on DLPI's reported address to avoid roto-tilling the world.
++ * (Note that isis_circuit_if_add on Solaris doesn't set the snpa.)
++ *
++ * Unfortunately, GLD is broken and doesn't provide the address after attach,
++ * so we need to be careful and use DL_PHYS_ADDR_REQ instead.
++ */
++ if (dlpiaddr (fd, circuit->u.bc.snpa) == -1)
++ {
++ zlog_warn ("open_dlpi_dev(): interface %s: unable to get MAC address",
++ circuit->interface->name);
++ close (fd);
++ return ISIS_WARNING;
++ }
++
++ /* Now bind to SAP 0. This gives us 802-type traffic. */
++ if (dlpibind (fd) == -1)
++ {
++ zlog_warn ("%s: cannot bind SAP 0 on %s", __func__,
++ circuit->interface->name);
++ close (fd);
++ return ISIS_WARNING;
++ }
++
++ /*
++ * Join to multicast groups according to
++ * 8.4.2 - Broadcast subnetwork IIH PDUs
++ */
++ retval = 0;
++ if (circuit->circuit_is_type & IS_LEVEL_1)
++ {
++ retval |= dlpimcast (fd, ALL_L1_ISS);
++ retval |= dlpimcast (fd, ALL_ISS);
++ }
++ if (circuit->circuit_is_type & IS_LEVEL_2)
++ retval |= dlpimcast (fd, ALL_L2_ISS);
++
++ if (retval != 0)
++ {
++ zlog_warn ("%s: unable to join multicast on %s", __func__,
++ circuit->interface->name);
++ close (fd);
++ return ISIS_WARNING;
++ }
++
++ /* Push on the packet filter to avoid stray 802 packets */
++ if (ioctl (fd, I_PUSH, "pfmod") == 0)
++ {
++ struct packetfilt pfil;
++
++ pfil.Pf_Priority = 0;
++ pfil.Pf_FilterLen = sizeof (pf_filter) / sizeof (u_short);
++ memcpy (pfil.Pf_Filter, pf_filter, sizeof (pf_filter));
++ ioctl (fd, PFIOCSETF, &pfil);
++ }
++
++ 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 = ISIS_OK;
++
++ if (isisd_privs.change (ZPRIVS_RAISE))
++ zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
++
++ retval = open_dlpi_dev (circuit);
++
++ if (retval != ISIS_OK)
++ {
++ zlog_warn ("%s: could not initialize the socket", __func__);
++ goto end;
++ }
++
++ if (circuit->circ_type == CIRCUIT_T_BROADCAST)
++ {
++ circuit->tx = isis_send_pdu_bcast;
++ circuit->rx = isis_recv_pdu_bcast;
++ }
++ else
++ {
++ zlog_warn ("isis_sock_init(): unknown circuit type");
++ retval = ISIS_WARNING;
++ goto end;
++ }
++
++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)
++{
++ struct pollfd fds[1];
++ struct strbuf ctlbuf, databuf;
++ int flags, retv;
++ dl_unitdata_ind_t *dui = (dl_unitdata_ind_t *)dlpi_ctl;
++
++ memset (fds, 0, sizeof (fds));
++ fds[0].fd = circuit->fd;
++ fds[0].events = POLLIN | POLLPRI;
++ if (poll (fds, 1, 0) <= 0)
++ return ISIS_WARNING;
++
++ memset (&ctlbuf, 0, sizeof (ctlbuf));
++ memset (&databuf, 0, sizeof (databuf));
++ ctlbuf.maxlen = sizeof (dlpi_ctl);
++ ctlbuf.buf = (void *)dlpi_ctl;
++ databuf.maxlen = sizeof (sock_buff);
++ databuf.buf = (void *)sock_buff;
++ flags = 0;
++ retv = getmsg (circuit->fd, &ctlbuf, &databuf, &flags);
++
++ if (retv < 0)
++ {
++ zlog_warn ("isis_recv_pdu_bcast: getmsg failed: %s",
++ safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ if (retv & (MORECTL | MOREDATA))
++ {
++ while (retv & (MORECTL | MOREDATA))
++ {
++ flags = 0;
++ retv = getmsg (circuit->fd, &ctlbuf, &databuf, &flags);
++ }
++ return ISIS_WARNING;
++ }
++
++ if (ctlbuf.len < DL_UNITDATA_IND_SIZE ||
++ dui->dl_primitive != DL_UNITDATA_IND)
++ return ISIS_WARNING;
++
++ if (dui->dl_src_addr_length != ETHERADDRL + 2 ||
++ dui->dl_src_addr_offset < DL_UNITDATA_IND_SIZE ||
++ dui->dl_src_addr_offset + dui->dl_src_addr_length > ctlbuf.len)
++ return ISIS_WARNING;
++
++ memcpy (ssnpa, (char *)dui + dui->dl_src_addr_offset +
++ (circuit->sap_length > 0 ? circuit->sap_length : 0), ETHERADDRL);
++
++ if (databuf.len < LLC_LEN || sock_buff[0] != ISO_SAP ||
++ sock_buff[1] != ISO_SAP || sock_buff[2] != 3)
++ return ISIS_WARNING;
++
++ stream_write (circuit->rcv_stream, sock_buff + LLC_LEN,
++ databuf.len - LLC_LEN);
++ stream_set_getp (circuit->rcv_stream, 0);
++
++ return ISIS_OK;
++}
++
++int
++isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
++{
++ dl_unitdata_req_t *dur = (dl_unitdata_req_t *)dlpi_ctl;
++ char *dstaddr;
++ u_short *dstsap;
++
++ stream_set_getp (circuit->snd_stream, 0);
++
++ memset (dur, 0, sizeof (*dur));
++ dur->dl_primitive = DL_UNITDATA_REQ;
++ dur->dl_dest_addr_length = ETHERADDRL + 2;
++ dur->dl_dest_addr_offset = sizeof (*dur);
++
++ dstaddr = (char *)(dur + 1);
++ if (circuit->sap_length < 0)
++ {
++ dstsap = (u_short *)(dstaddr + ETHERADDRL);
++ }
++ else
++ {
++ dstsap = (u_short *)dstaddr;
++ dstaddr += circuit->sap_length;
++ }
++ if (level == 1)
++ memcpy (dstaddr, ALL_L1_ISS, ETHERADDRL);
++ else
++ memcpy (dstaddr, ALL_L2_ISS, ETHERADDRL);
++ /* Note: DLPI SAP values are in host byte order */
++ *dstsap = stream_get_endp (circuit->snd_stream) + LLC_LEN;
++
++ sock_buff[0] = ISO_SAP;
++ sock_buff[1] = ISO_SAP;
++ sock_buff[2] = 0x03;
++ memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data,
++ stream_get_endp (circuit->snd_stream));
++ dlpisend (circuit->fd, dur, sizeof (*dur) + dur->dl_dest_addr_length,
++ sock_buff, stream_get_endp (circuit->snd_stream) + LLC_LEN, 0);
++ return ISIS_OK;
++}
+diff --git a/isisd/isis_flags.c b/isisd/isis_flags.c
+index 1dd5493..859facd 100644
+--- isisd/isis_flags.c
++++ isisd/isis_flags.c
+@@ -29,6 +29,13 @@
+ #include "isisd/isis_common.h"
+ #include "isisd/isis_flags.h"
+
++void
++flags_initialize (struct flags *flags)
++{
++ flags->maxindex = 0;
++ flags->free_idcs = NULL;
++}
++
+ int
+ flags_get_index (struct flags *flags)
+ {
+@@ -37,8 +44,7 @@
+
+ if (flags->free_idcs == NULL || flags->free_idcs->count == 0)
+ {
+- flags->maxindex++;
+- index = flags->maxindex;
++ index = flags->maxindex++;
+ }
+ else
+ {
+@@ -45,6 +51,7 @@
+ node = listhead (flags->free_idcs);
+ index = (int) listgetdata (node);
+ listnode_delete (flags->free_idcs, (void *) index);
++ index--;
+ }
+
+ return index;
+@@ -53,12 +60,18 @@
+ void
+ flags_free_index (struct flags *flags, int index)
+ {
++ if (index + 1 == flags->maxindex)
++ {
++ flags->maxindex--;
++ return;
++ }
++
+ if (flags->free_idcs == NULL)
+ {
+ flags->free_idcs = list_new ();
+ }
+
+- listnode_add (flags->free_idcs, (void *) index);
++ listnode_add (flags->free_idcs, (void *) (index + 1));
+
+ return;
+ }
+diff --git a/isisd/isis_flags.h b/isisd/isis_flags.h
+--- isisd/isis_flags.h
++++ isisd/isis_flags.h
+index 1dd5493..859facd 100644
+@@ -28,6 +28,7 @@
+ * the support will be achived using the newest drafts */
+ #define ISIS_MAX_CIRCUITS 32 /* = 1024 */ /*FIXME:defined in lsp.h as well */
+
++void flags_initialize (struct flags *flags);
+ struct flags *new_flags (int size);
+ int flags_get_index (struct flags *flags);
+ void flags_free_index (struct flags *flags, int index);
+diff --git a/isisd/isis_network.c b/isisd/isis_network.c
+deleted file mode 100644
+index 56459ec..0000000
+--- isisd/isis_network.c
++++ /dev/null
+@@ -1,643 +0,0 @@
+-/*
+- * IS-IS Rout(e)ing protocol - isis_network.c
+- *
+- * Copyright (C) 2001,2002 Sampo Saaristo
+- * Tampere University of Technology
+- * Institute of Communications Engineering
+- *
+- * 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>
+-#ifdef GNU_LINUX
+-#include <net/ethernet.h> /* the L2 protocols */
+-#else
+-#include <net/if.h>
+-#include <netinet/if_ether.h>
+-#endif
+-
+-#include "log.h"
+-#include "stream.h"
+-#include "if.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 "privs.h"
+-
+-extern struct zebra_privs_t isisd_privs;
+-
+-/*
+- * On linux we can use the packet(7) sockets, in other OSs we have to do with
+- * Berkley Packet Filter (BPF). Please tell me if you can think of a better
+- * way...
+- */
+-#ifdef GNU_LINUX
+-#include <netpacket/packet.h>
+-#else
+-#include <sys/time.h>
+-#include <sys/ioctl.h>
+-#include <net/bpf.h>
+-struct bpf_insn llcfilter[] = {
+- BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN), /* check first byte */
+- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5),
+- BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1),
+- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 3), /* check second byte */
+- BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2),
+- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), /* check third byte */
+- BPF_STMT (BPF_RET + BPF_K, (u_int) - 1),
+- BPF_STMT (BPF_RET + BPF_K, 0)
+-};
+-int readblen = 0;
+-u_char *readbuff = NULL;
+-#endif /* GNU_LINUX */
+-
+-/*
+- * Table 9 - Architectural constans for use with ISO 8802 subnetworks
+- * ISO 10589 - 8.4.8
+- */
+-
+-u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
+-u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
+-u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
+-u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
+-
+-#ifdef GNU_LINUX
+-static char discard_buff[8192];
+-#endif
+-static char sock_buff[8192];
+-
+-/*
+- * if level is 0 we are joining p2p multicast
+- * FIXME: and the p2p multicast being ???
+- */
+-#ifdef GNU_LINUX
+-static int
+-isis_multicast_join (int fd, int registerto, int if_num)
+-{
+- struct packet_mreq mreq;
+-
+- memset (&mreq, 0, sizeof (mreq));
+- mreq.mr_ifindex = if_num;
+- if (registerto)
+- {
+- mreq.mr_type = PACKET_MR_MULTICAST;
+- mreq.mr_alen = ETH_ALEN;
+- if (registerto == 1)
+- memcpy (&mreq.mr_address, ALL_L1_ISS, ETH_ALEN);
+- else if (registerto == 2)
+- memcpy (&mreq.mr_address, ALL_L2_ISS, ETH_ALEN);
+- else if (registerto == 3)
+- memcpy (&mreq.mr_address, ALL_ISS, ETH_ALEN);
+- else
+- memcpy (&mreq.mr_address, ALL_ESS, ETH_ALEN);
+-
+- }
+- else
+- {
+- mreq.mr_type = PACKET_MR_ALLMULTI;
+- }
+-#ifdef EXTREME_DEBUG
+- zlog_debug ("isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
+- "address = %02x:%02x:%02x:%02x:%02x:%02x",
+- fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1],
+- mreq.mr_address[2], mreq.mr_address[3], mreq.mr_address[4],
+- mreq.mr_address[5]);
+-#endif /* EXTREME_DEBUG */
+- if (setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
+- sizeof (struct packet_mreq)))
+- {
+- zlog_warn ("isis_multicast_join(): setsockopt(): %s", safe_strerror (errno));
+- return ISIS_WARNING;
+- }
+-
+- return ISIS_OK;
+-}
+-
+-static int
+-open_packet_socket (struct isis_circuit *circuit)
+-{
+- struct sockaddr_ll s_addr;
+- int fd, retval = ISIS_OK;
+-
+- fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
+- if (fd < 0)
+- {
+- zlog_warn ("open_packet_socket(): socket() failed %s",
+- safe_strerror (errno));
+- return ISIS_WARNING;
+- }
+-
+- /*
+- * Bind to the physical interface
+- */
+- memset (&s_addr, 0, sizeof (struct sockaddr_ll));
+- s_addr.sll_family = AF_PACKET;
+- s_addr.sll_protocol = htons (ETH_P_ALL);
+- s_addr.sll_ifindex = circuit->interface->ifindex;
+-
+- if (bind (fd, (struct sockaddr *) (&s_addr),
+- sizeof (struct sockaddr_ll)) < 0)
+- {
+- zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno));
+- return ISIS_WARNING;
+- }
+-
+- circuit->fd = fd;
+-
+- if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+- {
+- /*
+- * Join to multicast groups
+- * according to
+- * 8.4.2 - Broadcast subnetwork IIH PDUs
+- * FIXME: is there a case only one will fail??
+- */
+- if (circuit->circuit_is_type & IS_LEVEL_1)
+- {
+- /* joining ALL_L1_ISS */
+- retval = isis_multicast_join (circuit->fd, 1,
+- circuit->interface->ifindex);
+- /* joining ALL_ISS */
+- retval = isis_multicast_join (circuit->fd, 3,
+- circuit->interface->ifindex);
+- }
+- if (circuit->circuit_is_type & IS_LEVEL_2)
+- /* joining ALL_L2_ISS */
+- retval = isis_multicast_join (circuit->fd, 2,
+- circuit->interface->ifindex);
+- }
+- else
+- {
+- retval =
+- isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex);
+- }
+-
+- return retval;
+-}
+-
+-#else
+-
+-static int
+-open_bpf_dev (struct isis_circuit *circuit)
+-{
+- int i = 0, fd;
+- char bpfdev[128];
+- struct ifreq ifr;
+- u_int16_t blen;
+- int true = 1, false = 0;
+- struct timeval timeout;
+- struct bpf_program bpf_prog;
+-
+- do
+- {
+- (void) snprintf (bpfdev, sizeof (bpfdev), "/dev/bpf%d", i++);
+- fd = open (bpfdev, O_RDWR);
+- }
+- while (fd < 0 && errno == EBUSY);
+-
+- if (fd < 0)
+- {
+- zlog_warn ("open_bpf_dev(): failed to create bpf socket: %s",
+- safe_strerror (errno));
+- return ISIS_WARNING;
+- }
+-
+- zlog_debug ("Opened BPF device %s", bpfdev);
+-
+- memcpy (ifr.ifr_name, circuit->interface->name, sizeof (ifr.ifr_name));
+- if (ioctl (fd, BIOCSETIF, (caddr_t) & ifr) < 0)
+- {
+- zlog_warn ("open_bpf_dev(): failed to bind to interface: %s",
+- safe_strerror (errno));
+- return ISIS_WARNING;
+- }
+-
+- if (ioctl (fd, BIOCGBLEN, (caddr_t) & blen) < 0)
+- {
+- zlog_warn ("failed to get BPF buffer len");
+- blen = circuit->interface->mtu;
+- }
+-
+- readblen = blen;
+-
+- if (readbuff == NULL)
+- readbuff = malloc (blen);
+-
+- zlog_debug ("BPF buffer len = %u", blen);
+-
+- /* BPF(4): reads return immediately upon packet reception.
+- * Otherwise, a read will block until either the kernel
+- * buffer becomes full or a timeout occurs.
+- */
+- if (ioctl (fd, BIOCIMMEDIATE, (caddr_t) & true) < 0)
+- {
+- zlog_warn ("failed to set BPF dev to immediate mode");
+- }
+-
+-#ifdef BIOCSSEESENT
+- /*
+- * We want to see only incoming packets
+- */
+- if (ioctl (fd, BIOCSSEESENT, (caddr_t) & false) < 0)
+- {
+- zlog_warn ("failed to set BPF dev to incoming only mode");
+- }
+-#endif
+-
+- /*
+- * ...but all of them
+- */
+- if (ioctl (fd, BIOCPROMISC, (caddr_t) & true) < 0)
+- {
+- zlog_warn ("failed to set BPF dev to promiscuous mode");
+- }
+-
+- /*
+- * If the buffer length is smaller than our mtu, lets try to increase it
+- */
+- if (blen < circuit->interface->mtu)
+- {
+- if (ioctl (fd, BIOCSBLEN, &circuit->interface->mtu) < 0)
+- {
+- zlog_warn ("failed to set BPF buffer len (%u to %u)", blen,
+- circuit->interface->mtu);
+- }
+- }
+-
+- /*
+- * Set a timeout parameter - hope this helps select()
+- */
+- timeout.tv_sec = 600;
+- timeout.tv_usec = 0;
+- if (ioctl (fd, BIOCSRTIMEOUT, (caddr_t) & timeout) < 0)
+- {
+- zlog_warn ("failed to set BPF device timeout");
+- }
+-
+- /*
+- * And set the filter
+- */
+- memset (&bpf_prog, 0, sizeof (struct bpf_program));
+- bpf_prog.bf_len = 8;
+- bpf_prog.bf_insns = &(llcfilter[0]);
+- if (ioctl (fd, BIOCSETF, (caddr_t) & bpf_prog) < 0)
+- {
+- zlog_warn ("open_bpf_dev(): failed to install filter: %s",
+- safe_strerror (errno));
+- return ISIS_WARNING;
+- }
+-
+- assert (fd > 0);
+-
+- circuit->fd = fd;
+-
+- return ISIS_OK;
+-}
+-
+-#endif /* GNU_LINUX */
+-
+-/*
+- * Create the socket and set the tx/rx funcs
+- */
+-int
+-isis_sock_init (struct isis_circuit *circuit)
+-{
+- int retval = ISIS_OK;
+-
+- if (isisd_privs.change (ZPRIVS_RAISE))
+- zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
+-
+-#ifdef GNU_LINUX
+- retval = open_packet_socket (circuit);
+-#else
+- retval = open_bpf_dev (circuit);
+-#endif
+-
+- if (retval != ISIS_OK)
+- {
+- zlog_warn ("%s: could not initialize the socket", __func__);
+- goto end;
+- }
+-
+- if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+- {
+- circuit->tx = isis_send_pdu_bcast;
+- circuit->rx = isis_recv_pdu_bcast;
+- }
+- else if (circuit->circ_type == CIRCUIT_T_P2P)
+- {
+- circuit->tx = isis_send_pdu_p2p;
+- circuit->rx = isis_recv_pdu_p2p;
+- }
+- else
+- {
+- zlog_warn ("isis_sock_init(): unknown circuit type");
+- retval = ISIS_WARNING;
+- goto end;
+- }
+-
+-end:
+- if (isisd_privs.change (ZPRIVS_LOWER))
+- zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
+-
+- return retval;
+-}
+-
+-static inline int
+-llc_check (u_char * llc)
+-{
+- if (*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc + 2) != 3)
+- return 0;
+-
+- return 1;
+-}
+-
+-#ifdef GNU_LINUX
+-int
+-isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
+-{
+- int bytesread, addr_len;
+- struct sockaddr_ll s_addr;
+- u_char llc[LLC_LEN];
+-
+- addr_len = sizeof (s_addr);
+-
+- memset (&s_addr, 0, sizeof (struct sockaddr_ll));
+-
+- bytesread = recvfrom (circuit->fd, (void *) &llc,
+- LLC_LEN, MSG_PEEK,
+- (struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
+-
+- if (bytesread < 0)
+- {
+- zlog_warn ("isis_recv_packet_bcast(): fd %d, recvfrom (): %s",
+- circuit->fd, safe_strerror (errno));
+- zlog_warn ("circuit is %s", circuit->interface->name);
+- zlog_warn ("circuit fd %d", circuit->fd);
+- zlog_warn ("bytesread %d", bytesread);
+- /* get rid of the packet */
+- bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
+- return ISIS_WARNING;
+- }
+- /*
+- * Filtering by llc field, discard packets sent by this host (other circuit)
+- */
+- if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING)
+- {
+- /* Read the packet into discard buff */
+- bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
+- if (bytesread < 0)
+- zlog_warn ("isis_recv_pdu_bcast(): read() failed");
+- return ISIS_WARNING;
+- }
+-
+- /* on lan we have to read to the static buff first */
+- bytesread = recvfrom (circuit->fd, sock_buff, circuit->interface->mtu, 0,
+- (struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
+-
+- /* then we lose the LLC */
+- stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN);
+-
+- memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
+-
+- return ISIS_OK;
+-}
+-
+-int
+-isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
+-{
+- int bytesread, addr_len;
+- struct sockaddr_ll s_addr;
+-
+- memset (&s_addr, 0, sizeof (struct sockaddr_ll));
+- addr_len = sizeof (s_addr);
+-
+- /* we can read directly to the stream */
+- bytesread = stream_recvfrom (circuit->rcv_stream, circuit->fd,
+- circuit->interface->mtu, 0,
+- (struct sockaddr *) &s_addr,
+- (socklen_t *) &addr_len);
+-
+- if (s_addr.sll_pkttype == PACKET_OUTGOING)
+- {
+- /* Read the packet into discard buff */
+- bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
+- if (bytesread < 0)
+- zlog_warn ("isis_recv_pdu_p2p(): read() failed");
+- return ISIS_WARNING;
+- }
+-
+- /* If we don't have protocol type 0x00FE which is
+- * ISO over GRE we exit with pain :)
+- */
+- if (ntohs (s_addr.sll_protocol) != 0x00FE)
+- {
+- zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X",
+- ntohs (s_addr.sll_protocol));
+- return ISIS_WARNING;
+- }
+-
+- memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
+-
+- return ISIS_OK;
+-}
+-
+-int
+-isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
+-{
+- /* we need to do the LLC in here because of P2P circuits, which will
+- * not need it
+- */
+- int written = 1;
+- struct sockaddr_ll sa;
+-
+- stream_set_getp (circuit->snd_stream, 0);
+- memset (&sa, 0, sizeof (struct sockaddr_ll));
+- sa.sll_family = AF_PACKET;
+- sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
+- sa.sll_ifindex = circuit->interface->ifindex;
+- sa.sll_halen = ETH_ALEN;
+- if (level == 1)
+- memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
+- else
+- memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
+-
+- /* on a broadcast circuit */
+- /* first we put the LLC in */
+- sock_buff[0] = 0xFE;
+- sock_buff[1] = 0xFE;
+- sock_buff[2] = 0x03;
+-
+- /* then we copy the data */
+- memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data,
+- stream_get_endp (circuit->snd_stream));
+-
+- /* now we can send this */
+- written = sendto (circuit->fd, sock_buff,
+- stream_get_endp(circuit->snd_stream) + LLC_LEN, 0,
+- (struct sockaddr *) &sa, sizeof (struct sockaddr_ll));
+-
+- return ISIS_OK;
+-}
+-
+-int
+-isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
+-{
+-
+- int written = 1;
+- struct sockaddr_ll sa;
+-
+- stream_set_getp (circuit->snd_stream, 0);
+- memset (&sa, 0, sizeof (struct sockaddr_ll));
+- sa.sll_family = AF_PACKET;
+- sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
+- sa.sll_ifindex = circuit->interface->ifindex;
+- sa.sll_halen = ETH_ALEN;
+- if (level == 1)
+- memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
+- else
+- memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
+-
+-
+- /* lets try correcting the protocol */
+- sa.sll_protocol = htons (0x00FE);
+- written = sendto (circuit->fd, circuit->snd_stream->data,
+- stream_get_endp (circuit->snd_stream), 0,
+- (struct sockaddr *) &sa,
+- sizeof (struct sockaddr_ll));
+-
+- return ISIS_OK;
+-}
+-
+-#else
+-
+-int
+-isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
+-{
+- int bytesread = 0, bytestoread, offset, one = 1;
+- struct bpf_hdr *bpf_hdr;
+-
+- assert (circuit->fd > 0);
+-
+- if (ioctl (circuit->fd, FIONREAD, (caddr_t) & bytestoread) < 0)
+- {
+- zlog_warn ("ioctl() FIONREAD failed: %s", safe_strerror (errno));
+- }
+-
+- if (bytestoread)
+- {
+- bytesread = read (circuit->fd, readbuff, readblen);
+- }
+- if (bytesread < 0)
+- {
+- zlog_warn ("isis_recv_pdu_bcast(): read() failed: %s",
+- safe_strerror (errno));
+- return ISIS_WARNING;
+- }
+-
+- if (bytesread == 0)
+- return ISIS_WARNING;
+-
+- bpf_hdr = (struct bpf_hdr *) readbuff;
+-
+- assert (bpf_hdr->bh_caplen == bpf_hdr->bh_datalen);
+-
+- offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN;
+-
+- /* then we lose the BPF, LLC and ethernet headers */
+- stream_write (circuit->rcv_stream, readbuff + offset,
+- bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN);
+- stream_set_getp (circuit->rcv_stream, 0);
+-
+- memcpy (ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN,
+- ETHER_ADDR_LEN);
+-
+- if (ioctl (circuit->fd, BIOCFLUSH, &one) < 0)
+- zlog_warn ("Flushing failed: %s", safe_strerror (errno));
+-
+- return ISIS_OK;
+-}
+-
+-int
+-isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
+-{
+- int bytesread;
+-
+- bytesread = stream_read (circuit->rcv_stream, circuit->fd,
+- circuit->interface->mtu);
+-
+- if (bytesread < 0)
+- {
+- zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", safe_strerror (errno));
+- return ISIS_WARNING;
+- }
+-
+- return ISIS_OK;
+-}
+-
+-int
+-isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
+-{
+- struct ether_header *eth;
+- int written;
+-
+- stream_set_getp (circuit->snd_stream, 0);
+-
+- /*
+- * First the eth header
+- */
+- eth = (struct ether_header *) sock_buff;
+- if (level == 1)
+- memcpy (eth->ether_dhost, ALL_L1_ISS, ETHER_ADDR_LEN);
+- else
+- memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN);
+- memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN);
+- eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
+-
+- /*
+- * Then the LLC
+- */
+- sock_buff[ETHER_HDR_LEN] = ISO_SAP;
+- sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP;
+- sock_buff[ETHER_HDR_LEN + 2] = 0x03;
+-
+- /* then we copy the data */
+- memcpy (sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data,
+- stream_get_endp (circuit->snd_stream));
+-
+- /* now we can send this */
+- written = write (circuit->fd, sock_buff,
+- stream_get_endp (circuit->snd_stream)
+- + LLC_LEN + ETHER_HDR_LEN);
+-
+- return ISIS_OK;
+-}
+-
+-int
+-isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
+-{
+- return ISIS_OK;
+-}
+-
+-#endif /* GNU_LINUX */
+diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h
+index 29c7621..95c1ee4 100644
+--- isisd/isis_pdu.h
++++ isisd/isis_pdu.h
+@@ -24,6 +24,10 @@
+ #ifndef _ZEBRA_ISIS_PDU_H
+ #define _ZEBRA_ISIS_PDU_H
+
++#ifdef __SUNPRO_C
++#pragma pack(1)
++#endif
++
+ /*
+ * ISO 9542 - 7.5,7.6
+ *
+@@ -222,6 +226,10 @@ struct isis_partial_seqnum_hdr
+ };
+ #define ISIS_PSNP_HDRLEN 9
+
++#ifdef __SUNPRO_C
++#pragma pack()
++#endif
++
+ /*
+ * Function for receiving IS-IS PDUs
+ */
+diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c
+new file mode 100644
+index 0000000..8752dba
+--- /dev/null
++++ isisd/isis_pfpacket.c
+@@ -0,0 +1,373 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_pfpacket.c
++ *
++ * Copyright (C) 2001,2002 Sampo Saaristo
++ * Tampere University of Technology
++ * Institute of Communications Engineering
++ *
++ * 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/ethernet.h> /* the L2 protocols */
++#include <netpacket/packet.h>
++
++#include "log.h"
++#include "stream.h"
++#include "if.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 "privs.h"
++
++extern struct zebra_privs_t isisd_privs;
++
++/*
++ * Table 9 - Architectural constants for use with ISO 8802 subnetworks
++ * ISO 10589 - 8.4.8
++ */
++
++u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
++u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
++u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
++u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
++
++static char discard_buff[8192];
++static char sock_buff[8192];
++
++/*
++ * if level is 0 we are joining p2p multicast
++ * FIXME: and the p2p multicast being ???
++ */
++static int
++isis_multicast_join (int fd, int registerto, int if_num)
++{
++ struct packet_mreq mreq;
++
++ memset (&mreq, 0, sizeof (mreq));
++ mreq.mr_ifindex = if_num;
++ if (registerto)
++ {
++ mreq.mr_type = PACKET_MR_MULTICAST;
++ mreq.mr_alen = ETH_ALEN;
++ if (registerto == 1)
++ memcpy (&mreq.mr_address, ALL_L1_ISS, ETH_ALEN);
++ else if (registerto == 2)
++ memcpy (&mreq.mr_address, ALL_L2_ISS, ETH_ALEN);
++ else if (registerto == 3)
++ memcpy (&mreq.mr_address, ALL_ISS, ETH_ALEN);
++ else
++ memcpy (&mreq.mr_address, ALL_ESS, ETH_ALEN);
++
++ }
++ else
++ {
++ mreq.mr_type = PACKET_MR_ALLMULTI;
++ }
++#ifdef EXTREME_DEBUG
++ zlog_debug ("isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
++ "address = %02x:%02x:%02x:%02x:%02x:%02x",
++ fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1],
++ mreq.mr_address[2], mreq.mr_address[3], mreq.mr_address[4],
++ mreq.mr_address[5]);
++#endif /* EXTREME_DEBUG */
++ if (setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
++ sizeof (struct packet_mreq)))
++ {
++ zlog_warn ("isis_multicast_join(): setsockopt(): %s", safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ return ISIS_OK;
++}
++
++static int
++open_packet_socket (struct isis_circuit *circuit)
++{
++ struct sockaddr_ll s_addr;
++ int fd, retval = ISIS_OK;
++
++ fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
++ if (fd < 0)
++ {
++ zlog_warn ("open_packet_socket(): socket() failed %s",
++ safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ /*
++ * Bind to the physical interface
++ */
++ memset (&s_addr, 0, sizeof (struct sockaddr_ll));
++ s_addr.sll_family = AF_PACKET;
++ s_addr.sll_protocol = htons (ETH_P_ALL);
++ s_addr.sll_ifindex = circuit->interface->ifindex;
++
++ if (bind (fd, (struct sockaddr *) (&s_addr),
++ sizeof (struct sockaddr_ll)) < 0)
++ {
++ zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno));
++ return ISIS_WARNING;
++ }
++
++ circuit->fd = fd;
++
++ if (circuit->circ_type == CIRCUIT_T_BROADCAST)
++ {
++ /*
++ * Join to multicast groups
++ * according to
++ * 8.4.2 - Broadcast subnetwork IIH PDUs
++ * FIXME: is there a case only one will fail??
++ */
++ if (circuit->circuit_is_type & IS_LEVEL_1)
++ {
++ /* joining ALL_L1_ISS */
++ retval = isis_multicast_join (circuit->fd, 1,
++ circuit->interface->ifindex);
++ /* joining ALL_ISS */
++ retval = isis_multicast_join (circuit->fd, 3,
++ circuit->interface->ifindex);
++ }
++ if (circuit->circuit_is_type & IS_LEVEL_2)
++ /* joining ALL_L2_ISS */
++ retval = isis_multicast_join (circuit->fd, 2,
++ circuit->interface->ifindex);
++ }
++ else
++ {
++ retval =
++ isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex);
++ }
++
++ return retval;
++}
++
++/*
++ * Create the socket and set the tx/rx funcs
++ */
++int
++isis_sock_init (struct isis_circuit *circuit)
++{
++ int retval = ISIS_OK;
++
++ if (isisd_privs.change (ZPRIVS_RAISE))
++ zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
++
++ retval = open_packet_socket (circuit);
++
++ if (retval != ISIS_OK)
++ {
++ zlog_warn ("%s: could not initialize the socket", __func__);
++ goto end;
++ }
++
++ if (circuit->circ_type == CIRCUIT_T_BROADCAST)
++ {
++ circuit->tx = isis_send_pdu_bcast;
++ circuit->rx = isis_recv_pdu_bcast;
++ }
++ else if (circuit->circ_type == CIRCUIT_T_P2P)
++ {
++ circuit->tx = isis_send_pdu_p2p;
++ circuit->rx = isis_recv_pdu_p2p;
++ }
++ else
++ {
++ zlog_warn ("isis_sock_init(): unknown circuit type");
++ retval = ISIS_WARNING;
++ goto end;
++ }
++
++end:
++ if (isisd_privs.change (ZPRIVS_LOWER))
++ zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
++
++ return retval;
++}
++
++static inline int
++llc_check (u_char * llc)
++{
++ if (*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc + 2) != 3)
++ return 0;
++
++ return 1;
++}
++
++int
++isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
++{
++ int bytesread, addr_len;
++ struct sockaddr_ll s_addr;
++ u_char llc[LLC_LEN];
++
++ addr_len = sizeof (s_addr);
++
++ memset (&s_addr, 0, sizeof (struct sockaddr_ll));
++
++ bytesread = recvfrom (circuit->fd, (void *) &llc,
++ LLC_LEN, MSG_PEEK,
++ (struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
++
++ if (bytesread < 0)
++ {
++ zlog_warn ("isis_recv_packet_bcast(): fd %d, recvfrom (): %s",
++ circuit->fd, safe_strerror (errno));
++ zlog_warn ("circuit is %s", circuit->interface->name);
++ zlog_warn ("circuit fd %d", circuit->fd);
++ zlog_warn ("bytesread %d", bytesread);
++ /* get rid of the packet */
++ bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
++ return ISIS_WARNING;
++ }
++ /*
++ * Filtering by llc field, discard packets sent by this host (other circuit)
++ */
++ if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING)
++ {
++ /* Read the packet into discard buff */
++ bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
++ if (bytesread < 0)
++ zlog_warn ("isis_recv_pdu_bcast(): read() failed");
++ return ISIS_WARNING;
++ }
++
++ /* on lan we have to read to the static buff first */
++ bytesread = recvfrom (circuit->fd, sock_buff, circuit->interface->mtu, 0,
++ (struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
++
++ /* then we lose the LLC */
++ stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN);
++
++ memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
++
++ return ISIS_OK;
++}
++
++int
++isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
++{
++ int bytesread, addr_len;
++ struct sockaddr_ll s_addr;
++
++ memset (&s_addr, 0, sizeof (struct sockaddr_ll));
++ addr_len = sizeof (s_addr);
++
++ /* we can read directly to the stream */
++ bytesread = stream_recvfrom (circuit->rcv_stream, circuit->fd,
++ circuit->interface->mtu, 0,
++ (struct sockaddr *) &s_addr,
++ (socklen_t *) &addr_len);
++
++ if (s_addr.sll_pkttype == PACKET_OUTGOING)
++ {
++ /* Read the packet into discard buff */
++ bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
++ if (bytesread < 0)
++ zlog_warn ("isis_recv_pdu_p2p(): read() failed");
++ return ISIS_WARNING;
++ }
++
++ /* If we don't have protocol type 0x00FE which is
++ * ISO over GRE we exit with pain :)
++ */
++ if (ntohs (s_addr.sll_protocol) != 0x00FE)
++ {
++ zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X",
++ ntohs (s_addr.sll_protocol));
++ return ISIS_WARNING;
++ }
++
++ memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
++
++ return ISIS_OK;
++}
++
++int
++isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
++{
++ /* we need to do the LLC in here because of P2P circuits, which will
++ * not need it
++ */
++ int written = 1;
++ struct sockaddr_ll sa;
++
++ stream_set_getp (circuit->snd_stream, 0);
++ memset (&sa, 0, sizeof (struct sockaddr_ll));
++ sa.sll_family = AF_PACKET;
++ sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
++ sa.sll_ifindex = circuit->interface->ifindex;
++ sa.sll_halen = ETH_ALEN;
++ if (level == 1)
++ memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
++ else
++ memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
++
++ /* on a broadcast circuit */
++ /* first we put the LLC in */
++ sock_buff[0] = 0xFE;
++ sock_buff[1] = 0xFE;
++ sock_buff[2] = 0x03;
++
++ /* then we copy the data */
++ memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data,
++ stream_get_endp (circuit->snd_stream));
++
++ /* now we can send this */
++ written = sendto (circuit->fd, sock_buff,
++ stream_get_endp(circuit->snd_stream) + LLC_LEN, 0,
++ (struct sockaddr *) &sa, sizeof (struct sockaddr_ll));
++
++ return ISIS_OK;
++}
++
++int
++isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
++{
++
++ int written = 1;
++ struct sockaddr_ll sa;
++
++ stream_set_getp (circuit->snd_stream, 0);
++ memset (&sa, 0, sizeof (struct sockaddr_ll));
++ sa.sll_family = AF_PACKET;
++ sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
++ sa.sll_ifindex = circuit->interface->ifindex;
++ sa.sll_halen = ETH_ALEN;
++ if (level == 1)
++ memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
++ else
++ memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
++
++
++ /* lets try correcting the protocol */
++ sa.sll_protocol = htons (0x00FE);
++ written = sendto (circuit->fd, circuit->snd_stream->data,
++ stream_get_endp (circuit->snd_stream), 0,
++ (struct sockaddr *) &sa,
++ sizeof (struct sockaddr_ll));
++
++ return ISIS_OK;
++}
+diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h
+index 951a254..fc9f35f 100644
+--- isisd/isis_tlv.h
++++ isisd/isis_tlv.h
+@@ -152,6 +152,10 @@ struct lan_neigh
+ u_char LAN_addr[6];
+ };
+
++#ifdef __SUNPRO_C
++#pragma pack(1)
++#endif
++
+ /* struct for LSP entry */
+ struct lsp_entry
+ {
+@@ -161,6 +165,10 @@ struct lsp_entry
+ u_int16_t checksum;
+ } __attribute__ ((packed));
+
++#ifdef __SUNPRO_C
++#pragma pack()
++#endif
++
+ /* struct for checksum */
+ struct checksum
+ {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/70-isisd-trill.patch Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,8495 @@
+diff --git configure.ac configure.ac
+index 78198cb..12e9729 100755
+--- configure.ac
++++ configure.ac
+@@ -197,6 +197,10 @@ AC_ARG_ENABLE(watchquagga,
+ [ --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,
+ [ --disable-bgp-announce, turn off BGP route announcement])
+ AC_ARG_ENABLE(netlink,
+@@ -311,6 +315,30 @@ AC_SUBST(ISIS_TOPOLOGY_INCLUDES)
+ 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
+@@ -753,28 +781,31 @@ AC_SUBST(OTHER_METHOD)
+ dnl --------------------------
+ dnl Determine IS-IS I/O method
+ dnl --------------------------
+-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=isis_pfpacket.o
+-elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
+- AC_MSG_RESULT(DLPI)
+- ISIS_METHOD="isis_dlpi.o"
+-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=
++ 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=isis_pfpacket.o
++ elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
++ AC_MSG_RESULT(DLPI)
+ ISIS_METHOD="isis_dlpi.o"
+ else
+- AC_MSG_RESULT(BPF)
+- ISIS_METHOD="isis_bpf.o"
++ 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="isis_dlpi.o"
++ else
++ AC_MSG_RESULT(BPF)
++ ISIS_METHOD="isis_bpf.o"
++ fi
+ fi
+ fi
+ AC_SUBST(ISIS_METHOD)
+diff --git isisd/Makefile.am isisd/Makefile.am
+index 859facd..9adcc05 100644
+--- isisd/Makefile.am
++++ isisd/Makefile.am
+@@ -4,10 +4,11 @@ INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib \
+ @ISIS_TOPOLOGY_INCLUDES@
+ DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+ INSTALL_SDATA=@INSTALL@ -m 600
+-LIBS = @LIBS@
++LIBS = @LIBS@ @ISIS_LIBS@
+ noinst_LIBRARIES = libisis.a
+-sbin_PROGRAMS = isisd
++sbin_PROGRAMS = @ISIS_TARGETS@
+ SUBDIRS = topology
++EXTRA_PROGRAMS = isisd trilld
+
+ isis_method = @ISIS_METHOD@
+
+@@ -23,16 +24,24 @@ noinst_HEADERS = \
+ 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_main.c $(libisis_a_SOURCES) isis_trilldummy.c
+
+-isisd_LDADD = $(isis_method) @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
++isisd_LDADD = $(isis_method) @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@ @LIBM@
+
+ isisd_DEPENDENCIES = $(isis_method)
+
+-EXTRA_DIST = isis_bpf.c isis_dlpi.c isis_pfpacket.c
++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,8 @@
+ */
+
+ #include <zebra.h>
++#include <vty.h>
++
+ #include <net/if.h>
+ #include <netinet/if_ether.h>
+ #include <sys/types.h>
+@@ -301,7 +303,7 @@ dlpiaddr (int fd, u_char *addr)
+ 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;
+@@ -313,20 +315,21 @@ open_dlpi_dev (struct isis_circuit *circuit)
+ 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);
+ }
+@@ -402,8 +405,8 @@ open_dlpi_dev (struct isis_circuit *circuit)
+ 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;
+ }
+@@ -471,9 +474,9 @@ open_dlpi_dev (struct isis_circuit *circuit)
+ sioc.ic_timout = 5;
+ sioc.ic_len = sizeof (struct packetfilt);
+ sioc.ic_dp = (char *)&pfil;
+- if (ioctl (fd, I_STR, &sioc) == -1)
++ if (ioctl (fd, I_STR, &sioc) == -1)
+ zlog_warn("%s: could not perform PF_IOCSETF on %s",
+- __func__, circuit->interface->name);
++ __func__, circuit->interface->name);
+ }
+
+ circuit->fd = fd;
+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 @@ struct zebra_privs_t isisd_privs = {
+ .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 @@ reload ()
+ 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 @@ void
+ sighup (void)
+ {
+ zlog_debug ("SIGHUP received");
++#ifdef HAVE_TRILL
++ if (!trill_reload())
++#endif
+ reload ();
+
+ return;
+@@ -227,6 +245,11 @@ main (int argc, char **argv, char **envp)
+ 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 @@ main (int argc, char **argv, char **envp)
+ 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 ();
+
+@@ -337,7 +376,8 @@ main (int argc, char **argv, char **envp)
+ daemon (0, 0);
+
+ /* 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.c lib/command.c
+index 270bf0d..2c8d752 100644
+--- lib/command.c
++++ lib/command.c
+@@ -193,8 +193,8 @@ install_node (struct cmd_node *node,
+ static int
+ cmp_node (const void *p, const void *q)
+ {
+- const struct cmd_element *a = *(struct cmd_element **)p;
+- const struct cmd_element *b = *(struct cmd_element **)q;
++ const struct cmd_element *a = *(struct cmd_element * const *)p;
++ const struct cmd_element *b = *(struct cmd_element * const *)q;
+
+ return strcmp (a->string, b->string);
+ }
+@@ -202,8 +202,8 @@ cmp_node (const void *p, const void *q)
+ static int
+ cmp_desc (const void *p, const void *q)
+ {
+- const struct desc *a = *(struct desc **)p;
+- const struct desc *b = *(struct desc **)q;
++ const struct desc *a = *(struct desc * const *)p;
++ const struct desc *b = *(struct desc * const *)q;
+
+ return strcmp (a->cmd, b->cmd);
+ }
+@@ -491,8 +491,11 @@ install_element (enum node_type ntype, struct cmd_element *cmd)
+
+ vector_set (cnode->cmd_vector, cmd);
+
+- cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc);
+- cmd->cmdsize = cmd_cmdsize (cmd->strvec);
++ if (cmd->strvec == NULL)
++ {
++ cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc);
++ cmd->cmdsize = cmd_cmdsize (cmd->strvec);
++ }
+ }
+
+ static unsigned char itoa64[] =
+@@ -3497,6 +3500,8 @@ DEFUN (no_banner_motd,
+ void
+ host_config_set (char *filename)
+ {
++ if (host.config != NULL)
++ XFREE (MTYPE_HOST, host.config);
+ host.config = XSTRDUP (MTYPE_HOST, filename);
+ }
+
+diff --git lib/command.h lib/command.h
+index a725378..fefa6d3 100644
+--- lib/command.h
++++ lib/command.h
+@@ -302,6 +302,8 @@ struct desc
+ #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"
+@@ -359,5 +361,5 @@ extern void host_config_set (char *);
+ extern void print_version (const char *);
+
+ /* struct host global, ick */
+-extern struct host host;
++extern struct host host;
+ #endif /* _ZEBRA_COMMAND_H */
+diff --git lib/linklist.c lib/linklist.c
+index 11e16a8..a16e9e1 100644
+--- lib/linklist.c
++++ lib/linklist.c
+@@ -65,7 +65,9 @@ void
+ listnode_add (struct list *list, void *val)
+ {
+ struct listnode *node;
+-
++
++ assert (val != NULL);
++
+ node = listnode_new ();
+
+ node->prev = list->tail;
+@@ -91,7 +93,9 @@ listnode_add_sort (struct list *list, void *val)
+ {
+ struct listnode *n;
+ struct listnode *new;
+-
++
++ assert (val != NULL);
++
+ new = listnode_new ();
+ new->data = val;
+
+@@ -130,7 +134,9 @@ void
+ listnode_add_after (struct list *list, struct listnode *pp, void *val)
+ {
+ struct listnode *nn;
+-
++
++ assert (val != NULL);
++
+ nn = listnode_new ();
+ nn->data = val;
+
+@@ -158,6 +164,7 @@ listnode_add_after (struct list *list, struct listnode *pp, void *val)
+
+ pp->next = nn;
+ }
++ list->count++;
+ }
+
+
+@@ -265,7 +272,9 @@ void
+ list_add_node_prev (struct list *list, struct listnode *current, void *val)
+ {
+ struct listnode *node;
+-
++
++ assert (val != NULL);
++
+ node = listnode_new ();
+ node->next = current;
+ node->data = val;
+@@ -286,7 +295,9 @@ void
+ list_add_node_next (struct list *list, struct listnode *current, void *val)
+ {
+ struct listnode *node;
+-
++
++ assert (val != NULL);
++
+ node = listnode_new ();
+ node->prev = current;
+ node->data = val;
+diff --git lib/memory.c lib/memory.c
+index eb67072..9ed5e10 100644
+--- lib/memory.c
++++ lib/memory.c
+@@ -501,7 +501,7 @@ mtype_memstr (char *buf, size_t len, unsigned long bytes)
+ * Just hacked to make it not warn on 'smaller' machines.
+ * Static compiler analysis should mean no extra code
+ */
+- if (bytes & (1 << (sizeof (unsigned long) >= 8 ? 39 : 0)))
++ if (bytes & (1UL << (sizeof (unsigned long) >= 8 ? 39 : 0)))
+ t++;
+ snprintf (buf, len, "%4d TiB", t);
+ }
+diff --git lib/memtypes.c lib/memtypes.c
+index c7028c9..476ad35 100644
+--- lib/memtypes.c
++++ lib/memtypes.c
+@@ -225,20 +225,32 @@ struct memory_list memory_list_ospf6[] =
+
+ 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 lib/memtypes.h
+deleted file mode 100644
+index 2d90e80..0000000
+--- lib/memtypes.h
++++ /dev/null
+@@ -1,206 +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_PEER,
+- MTYPE_BGP_PEER_HOST,
+- MTYPE_PEER_GROUP,
+- MTYPE_PEER_DESC,
+- 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_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
+@@ -12,7 +12,7 @@ pkg_depends := $(pkg_names:%=depend.%)
+ 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 &'
++ 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>