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