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