7066915 Move Quagga to Userland
authorBrian Utterback <Brian.Utterback@Oracle.COM>
Mon, 18 Jul 2011 12:08:25 -0700
changeset 417 7c10b5cba79b
parent 416 d3ce52a8aecd
child 418 5b95e3689883
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
components/meta-packages/history/SUNWquagga.p5m
components/quagga/Makefile
components/quagga/Solaris/README.Solaris
components/quagga/Solaris/exec_attr
components/quagga/Solaris/ospfd.HA.conf.sample
components/quagga/Solaris/quagga.1m
components/quagga/Solaris/quaggaadm
components/quagga/Solaris/quaggaadm.1m
components/quagga/patches/10-sunw-smf.patch
components/quagga/patches/15-privs-ipinst.patch
components/quagga/patches/20-privs-bgpd.patch
components/quagga/patches/25-isisd-dlpi.patch
components/quagga/patches/30-ospfd-nssa-asbr.patch
components/quagga/patches/35-ospfd-spf-sort.patch
components/quagga/patches/40-bgp-capab-cleanup.patch
components/quagga/patches/45-bgpd-capab-typo.patch
components/quagga/patches/50-bgpd-nosub.patch
components/quagga/patches/55-bgpd-rm-assert.patch
components/quagga/patches/60-bgp-comm-crash.patch
components/quagga/patches/65-isisd-iso-checksum.patch
components/quagga/patches/70-isisd-trill.patch
components/quagga/patches/75-privs-basicprivset.patch
components/quagga/patches/80-ripngd-getopt.patch
components/quagga/patches/85-remove-fwding-dependency.patch
components/quagga/patches/87-ospfd-apiserver.patch
components/quagga/patches/90-fix-manpages
components/quagga/quagga.license
components/quagga/quagga.p5m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/meta-packages/history/SUNWquagga.p5m	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+#
+# Legacy package information for renamed SUNWntpr and SUNWntpu packages
+#
+
+set name=pkg.fmri value=pkg:/[email protected],5.11-0.133
+set name=pkg.renamed value=true
+
+set name=org.opensolaris.consolidation value=$(CONSOLIDATION)
+
+depend fmri=system/network/routing/[email protected] type=require
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/Makefile	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,78 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../make-rules/shared-macros.mk
+
+COMPONENT_NAME=		quagga
+COMPONENT_VERSION=	0.99.8
+IPS_COMPONENT_VERSION=	$(COMPONENT_VERSION)
+COMPONENT_SRC=		$(COMPONENT_NAME)-$(COMPONENT_VERSION)
+COMPONENT_ARCHIVE=	$(COMPONENT_SRC).tar.gz
+COMPONENT_ARCHIVE_HASH=	sha1:9576d0ac266d173d2a90b8c6f72da8c6b43583d7
+COMPONENT_ARCHIVE_URL=	http://www.quagga.net/download/attic/$(COMPONENT_ARCHIVE)
+PATCH_LEVEL = 0
+
+# These options are carried over from the SFW consolidation. Using -xO4 was
+# explicitly commented out, but no reason was docuemnted.
+
+studio_OPT = -xO3
+
+LDFLAGS +=-lumem
+CFGLOCALSTATEDIR=/system/volatile/quagga
+CFGSYSCONFDIR=  $(ETCDIR)/quagga
+
+CFLAGS += -xspace -Xa
+CFLAGS += -g -xdebugformat=dwarf -O
+CFLAGS += -errtags=yes -xc99=%all
+CFLAGS += -erroff=E_TRAILING_COMMA_IN_ENUM
+CFLAGS += -erroff=E_STATEMENT_NOT_REACHED
+CFLAGS += -erroff=E_EMPTY_TRANSLATION_UNIT
+
+include ../../make-rules/prep.mk
+include ../../make-rules/configure.mk
+include ../../make-rules/ips.mk
+
+CONFIGURE_OPTIONS +=	--with-tags=""
+CONFIGURE_OPTIONS +=	--with-cflags="$(CFLAGS)"
+CONFIGURE_OPTIONS +=	--enable-opaque-lsa
+CONFIGURE_OPTIONS +=	--localstatedir=$(CFGLOCALSTATEDIR)
+CONFIGURE_OPTIONS +=	--sysconfdir=$(CFGSYSCONFDIR)
+CONFIGURE_OPTIONS +=	--includedir=$(CONFIGURE_INCLUDEDIR)
+CONFIGURE_OPTIONS +=	--infodir=$(CONFIGURE_INFODIR)
+CONFIGURE_OPTIONS +=	--enable-isisd
+CONFIGURE_OPTIONS +=	--enable-trill
+CONFIGURE_OPTIONS +=	--disable-watchquagga
+CONFIGURE_OPTIONS +=	--enable-ospf6d
+CONFIGURE_OPTIONS +=	--enable-ripngd
+CONFIGURE_OPTIONS +=	--enable-user=root
+CONFIGURE_OPTIONS +=	--enable-group=root
+
+BUILD_PKG_DEPENDENCIES =	$(BUILD_TOOLS)
+
+build:		$(BUILD_32)
+install:	$(INSTALL_32)
+test:	$(NO_TESTS)
+
+include ../../make-rules/depend.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/Solaris/README.Solaris	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,503 @@
+#
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+#
+#ident	"@(#)README.Solaris	1.2	11/01/03 SMI"
+
+		SOLARIS' OFFERING OF OPENSOURCE QUAGGA SOFTWARE
+	       ************************************************
+Quagga(version 0.99.4) in Solaris is comprised of the following packages:
+
+SUNWquagga-daemons:
+ - Provides the Quagga daemons and the quaggaadm utility. 
+   The daemons installed by this package are:
+   
+   ripd, ripngd, ospfd, ospf6d, bgpd, and zebra(the routing manager daemon)
+                    
+   This package has dependencies on SUNWquaggar and SUNWquagga-libs
+
+SUNWquaggar: 
+ - Provides sample configuration files in /etc/quagga/, this README.Solaris
+   file and SMF manifests for Quagga daemons.
+
+SUNWquagga-libs:
+ - Provides Quagga-specific dynamic libraries used by the Quagga daemons.
+
+SUNWquagga-dev:
+ - Provides header files, archive libraries and libtool files required for
+   building code using the Quagga libraries. Note that these interfaces are
+   External to Solaris and come without API stability guarantees. See also the
+   attributes (5) manual page.
+ 
+SUNWquaggaS
+
+ - Provides the sources from which this release of the SUNWquagga packages
+   were built. You may need to install either the Workshop compilers or gcc
+   to compile the source.  In order to compile Quagga's vtysh utility, you
+   will need Readline libraries, which are freely available on the web.
+
+
+Location of Installed Files and Directories
+===========================================
+
+Modules of Quagga				Location
+-------------------------------------------------------------
+Sample configuration files			/etc/quagga
+Daemon binaries(zebra,ospfd, ospf6d, ripd, 
+                ripngd, bgpd)			/usr/sbin
+quaggaadm (formerly zebraadm)			/usr/sbin
+daemon start/stop scripts			/lib/svc/method 
+SMF manifests					/lib/svc/manifest/network/routing
+Quagga Info documentation			/usr/share/info
+Manual Pages					/usr/share/man/man1m
+Libraries					/usr/lib
+Development headers				/usr/include/quagga
+
+
+Upgrading from SUNWzebra
+========================
+
+Previously, the Zebra routing protocol suite was delivered as part of the
+SFW consolidation.  This has now been replaced with Quagga, and Zebra
+routing configuration can be migrated easily, either explicitly by running
+"routeadm -u" (see routeadm (1M)), or by rebooting the system. The
+appropriate configuration files for the daemons used will be migrated to
+Quagga, and the equivalent Quagga SMF services will be enabled. 
+
+
+Incompatibilities of Quagga  
+============================
+
+You may use in.routed(1M) on other systems on your network, but you must not
+run in.routed or in.rdisc on the same system that is configured to run
+Quagga. Quagga is incompatible with the Solaris IP Multipathing
+(in.mpathd(1M)) feature.  Do not enable IPMP on a system running Quagga. If
+you have a machine set up with IPMP and wish to run Quagga on it, please
+unconfigure IPMP. See
+
+IPMP details at:
+Solaris 10 System Administrator Collection >>
+	System Administration Guide: IP Services >>
+		Part VI IP Network Multipathing (IPMP)  >>
+			30. Administering IPMP (Task)) 
+
+
+IFF_NORTEXCH Interface Flag Support
+====================================
+
+Currently Quagga is not aware of the IFF_NORTEXCH interface flag, so if
+you're trying to fence off interfaces from the intrusion of unwanted routing
+protocols, make sure you don't configure those interfaces in Quagga.
+
+
+Support Level of Quagga Software
+================================
+
+The contents of SUNWquaggar, SUNWquagga-daemons are provided with full Level
+1 support in accordance with your current software support agreement. This
+support includes Sun's global 24/7 sustaining model.
+
+
+Configuring a Multi-homed Host as a Router Using Quagga
+==========================================================
+
+Steps:
+1. Create the appropriate  daemon configuration file in /etc/quagga.
+   Sample configuration files have been provided in that directory.  
+
+2.  Enable forwarding 
+		routeadm -e ipv4-forwarding
+		routeadm -u
+
+    Disable IPMP if machine is set up with it. To do this, you will 
+    have to undo all the things you had done to configure IPMP
+    on your system. Please see :
+	http://docs.sun.com
+	Product Categories>> Solaris >> Solaris 10
+	Solaris 10 System Administration Guide:IP services, Chapter 30
+
+    for configurations details of IPMP.
+
+3. Ensure that IPMP is disabled, and that the svc:/network/routing/route
+   and svc:/network/routing/rdisc SMF services are disabled.
+
+   Also it is important to note that each daemon is invoked with
+   arguments to disable remote Telnet access to the daemons as it is a
+   security risk. Please do not edit these configuration parameters that
+   comprise part of the daemon-args property for each service.
+
+   Pick the appropriate SMF service for the routing daemon that
+   you wish to start. To enable a Quagga daemon service, type the following
+   routeadm command:
+
+  # routeadm -s routing-svcs="<routing daemon svc>" -e ipv4-routing -u
+  
+  or
+
+  # svcadm enable <routing daemon svc>
+
+  Example: To enable the ospfd daemon, type the command:
+
+  # routeadm -s routing-svcs=ospf:quagga -e ipv4-routing -u
+
+  To enable the ospf6d daemon, type the command:
+
+  # routeadm -s routing-svcs=ospf6:quagga -e ipv6-routing -u
+
+
+Editing the Daemon Arguments
+----------------------------
+You can change the arguments used to invoke the Quagga routing
+daemons by modifying the service properties (listed by
+running "routeadm -l <routing daemon svc>"). For example,
+setting
+
+   # routeadm -m ripng:quagga config_file=/path2/ripng.conf
+
+sets an alternate configuration file.
+
+Monitoring, Debugging and Reconfiguring Quagga Daemons Interactively
+====================================================================
+
+Quagga provides a Telnet UI so that the user can access the daemons in
+real-time.  This interface is disabled by default for all daemons, but can
+be enabled by changing the daemon-args property of Quagga services to a suitable
+value, such as "-A 127.0.0.1":
+
+  # routeadm -m ospf:quagga vty_address="127.0.0.1"
+
+This user interface allows one to connect to each daemon, monitor the
+daemon, tag debugging parameters, and reconfigure the parameters of the
+running daemon. We have provided this facility with a wrapper utility called
+quaggaadm (formerly zebraadm).
+
+To access a particular daemon type
+	/usr/sbin/quaggaadm zebra - to access a running zebra daemon
+	/usr/sbin/quaggaadm ospfd - to access a running ospfd daemon
+	/usr/sbin/quaggaadm ripd - to access a running ripd daemon
+	/usr/sbin/quaggaadm bgpd - to access a running bgpd daemon
+
+*****WARNING*****WARNING****WARNING********
+
+By default, if the daemon-args are not set so as to restrict access, Quagga
+allows a user to remotely access the daemons via the Telnet UI. We STRONGLY
+RECOMMEND AGAINST remote Telnet access of the daemons, as it leaves the
+system vulnerable to security holes. To avoid leaving your system
+vulnerable, all daemons must be invoked with "-A 127.0.0.1" option, as shown
+in the example above where routeadm is used to modify the 'daemon-args'
+property.
+
+*****WARNING*****WARNING****WARNING********
+
+
+Disabling Quagga Daemons on a System
+====================================
+
+If you have enabled Quagga routing daemons as discussed above, and now wish
+to disable them, this can be done generally with:
+
+	# routeadm -d ipv4-routing -u
+
+or
+
+	# routeadm -d ipv6-routing -u
+
+as appropriate. One may also disable just specific daemons with:
+
+	# svcadm disable <daemon service>
+
+High-Availability Networking for Hosts with Quagga
+==================================================
+
+The OSPF-MP (OSPF Multi-Pathing) feature is a layer 3 solution to achieve
+network connectivity redundancy on servers. It uses the popular technique of
+advertising loopback-hosted virtual addresses using a routing protocol, in
+this case the OSPF routing protocol.
+
+The OSPF-MP feature is meant to be enabled on multihomed servers to
+implement an HA solution based on the OSPF protocol. Note that the server's
+interfaces *do not require forwarding to be enabled* for the functioning of
+this feature. The feature does require, though, that
+ip_strict_dst_multihoming not be enabled. The OSPF-MP feature can be
+achieved by configuring Quagga appropriately on a server.
+
+ Configuration
+ =============
+
+	  | loopback virtual addresses:
+          | lo0:1, lo0:2.... lo0:n
+	  |
+	---------------------------------------
+	| server with OSPF-MP feature enabled |
+	---------------------------------------
+	      |	   		   |
+	   ====== subnet A	 ===== subnet B
+	      |                    |
+	----------------------------------
+        |     OSPF      router           |
+	----------------------------------
+		   | 
+		 ======  subnet C
+		   |  
+                ----------
+		| client |
+		----------
+
+ 
+Setting up a Multi-Homed Host with OSPF-MP
+==========================================
+
+Steps
+1.Configure loopback aliases on the machine. Following is an
+  example: 
+	#ifconfig lo0:1 inet plumb 172.16.3.91/32 up
+
+  To have these loopback aliases plumb up across boots, create the
+  corresponding /etc/hostname.lo0:<alias#> files. For the above
+  example loopback alias case, the corresponding /etc/hostname.lo0:1
+  file would have the following entry:
+	172.16.3.91 netmask 255.255.255.255 up
+  
+2. Copy over the OSPF-MP sample configuration files:
+          cd /etc/quagga
+          cp server-zebra.HA.conf.sample zebra.conf
+          cp server-ospfd.HA.conf.sample ospdf.conf
+
+3  Edit the zebra and ospfd configuration files appropriately  
+
+4. Disable forwarding on your server.
+		routeadm -d ipv4-forwarding
+		routeadm -u
+
+5. Disable IPMP if machine is set up with it. To do this you will
+   have to undo all the things you had done to configure IPMP on your
+   system. Please see:
+        http://docs.sun.com
+        Product Categories>> Solaris >> Solaris 10
+        Solaris 10 System Administration Guide:IP services, Chapter 30
+
+    for configuration details of IPMP.
+
+6. Enable the OSPF-MP service at boot time, type the following
+   routeadm command:
+    # routeadm -s routing-svcs=ospf:quagga -e ipv4-routing -u
+
+7. Verify that the loopback hosted addresses are being correctly 
+   advertised by OSPF on the server, use the following snoop command:  
+	snoop -d <device> -rv ospf
+
+Following is the snoop output on a server that is enabled with OSPF-MP, and
+is configured with the loopback alias of the example case above:
+
+ETHER:  ----- Ether Header -----
+ETHER:  
+ETHER:  Packet 8 arrived at 16:23:57.00008
+ETHER:  Packet size = 82 bytes
+ETHER:  Destination = 1:0:5e:0:0:5, (multicast)
+ETHER:  Source      = 0:d0:b7:b9:ac:b2, 
+ETHER:  Ethertype = 0800 (IP)
+ETHER:  
+IP:   ----- IP Header -----
+IP:   
+IP:   Version = 4
+IP:   Header length = 20 bytes
+IP:   Type of service = 0xc0
+IP:         xxx. .... = 6 (precedence)
+IP:         ...0 .... = normal delay
+IP:         .... 0... = normal throughput
+IP:         .... .0.. = normal reliability
+IP:         .... ..0. = not ECN capable transport
+IP:         .... ...0 = no ECN congestion experienced
+IP:   Total length = 68 bytes
+IP:   Identification = 41685
+IP:   Flags = 0x0
+IP:         .0.. .... = may fragment
+IP:         ..0. .... = last fragment
+IP:   Fragment offset = 0 bytes
+IP:   Time to live = 1 seconds/hops
+IP:   Protocol = 89 (OSPF)
+IP:   Header checksum = 2ac5
+IP:   Source address = 10.1.1.1, 10.1.1.1
+IP:   Destination address = 224.0.0.5, 224.0.0.5
+IP:   No options
+IP:   
+OSPF:  ----- OSPF Header -----
+OSPF:  
+OSPF:  Version = 2
+OSPF:  Type = Hello
+OSPF:  Router ID = 10.1.2.1
+OSPF:  Area ID = 0.0.0.1
+OSPF:  Checksum = 0x2b27
+OSPF:  Auth = None
+OSPF HELLO:  ----- Hello Packet -----
+OSPF HELLO:  
+OSPF HELLO:  Options = E
+OSPF HELLO:  Mask = 255.255.255.0
+OSPF HELLO:  Hello interval = 10
+OSPF HELLO:  Priority = 1
+OSPF HELLO:  Dead interval = 40
+OSPF HELLO:  Designated Router = 10.1.1.2
+OSPF HELLO:  Backup Designated Router = 10.1.1.1
+OSPF HELLO:  Neighbor: 172.16.3.91
+ 
+
+Example configuration case on a server with OSPF-MP feature
+-----------------------------------------------------------
+Given a server with the following ifconfig output:
+
+# ifconfig -a
+lo0: flags=1000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4> mtu 8232 index 1
+        inet 127.0.0.1 netmask ff000000 
+lo0:1: flags=1000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4> mtu 8232 index 1
+        inet 172.16.3.91 netmask ffffffff 
+hme1: flags=1100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4> mtu
+1500 index 3  inet 10.10.48.91
+netmask ffffff00 broadcast 10.10.48.255 ether 8:0:20:d9:53:71 
+qfe0: flags=1100843<UP,BROADCAST,RUNNING,MULTICAST,ROUTER,IPv4> mtu 1500 index 4  inet 10.11.48.91
+netmask ffffff00 broadcast 10.11.48.255 ether 0:3:ba:17:4d:47 
+
+Its ospfd and zebra config files for OSPF-MP would be the following:
+::::::::::::::
+zebra.conf
+::::::::::::::
+!
+! Zebra configuration saved from vty
+!   2004/03/08 18:35:11
+!
+hostname test-machine
+password zebra
+log file /var/tmp/zebra.log
+service advanced-vty
+!
+interface lo0
+interface hme1
+ link-detect
+interface qfe0
+ link-detect
+!
+line vty
+!
+::::::::::::::
+ospfd.conf
+::::::::::::::
+!
+! Zebra configuration saved from vty
+!   2004/03/15 16:23:35
+!
+hostname test-machine 
+password zebra
+log file /var/tmp/ospf.log
+service advanced-vty
+!
+router ospf
+ ospf router-id 10.10.48.91 
+ redistribute connected
+ network 10.10.48.0/24 area 1
+ network 10.11.48.0/24 area 1
+!
+line vty
+exec-timeout 0 0
+!
+# 
+
+Troubleshooting the OSPF-MP Feature 
+===================================
+
+Use the following monitoring and debugging commands on a running
+ospfd daemon via the telnet command (ie "/usr/sbin/quaggaadm ospfd").
+
+Monitoring Commands for the ospfd Daemon
+----------------------------------------
+  show ip ospf
+  show ip ospf neighbor
+  show history
+  show debugging ospf
+  show ip ospf interface [INTERFACE]
+  show running-config
+  show startup-config 
+
+Debug Commands for the ospfd Daemon
+-----------------------------------
+  debug ospf event
+  debug ospf ism
+  debug ospf ism (status|events|timers)
+  debug ospf lsa
+  debug ospf lsa (generate|flooding|refresh)
+  debug ospf nsm
+  debug ospf nsm (status|events|timers)
+  debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all)
+  debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv)
+     (detail|)
+  debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all)
+    (send|recv|detail)
+  debug ospf zebra
+  debug ospf zebra (interface|redistribute)
+
+Similarly, use the following monitoring and debugging commands on a running
+zebra daemon via the telnet command ( ie "/usr/sbin/zebraadm zebra").
+
+Monitor Commands for the zebra Daemon
+--------------------------------------
+  show history
+  show debugging zebra
+  show interface [IFNAME]
+  show ip forwarding
+  show running-config
+  show startup-config
+
+Debug Commands for the zebra Daemon
+-----------------------------------
+  debug zebra events
+  debug zebra kernel
+  debug zebra packet
+  debug zebra packet (recv|send)
+  debug zebra packet (recv|send) detail
+
+
+Fine-tuning the OSPF-MP Feature by Customizing the OSPF Timers
+==============================================================
+Use specific interface level configuration subcommands of Telnet UI 
+to fine-tune the timers of OSPF daemon. To get to the interface level
+configuration mode, type:
+
+	/usr/sbin/quaggaadm ospfd
+	Password:<type password that is set in the ospfd.conf file>
+	<hostname>#configure terminal
+	<hostname>(config)# interface <interface name>
+	<hostname>(config-if)#
+
+The appropriate subcommands to customize the timers are:
+
+ ip ospf dead-interval <1-65535>
+ ip ospf hello-interval <1-65535>
+ ip ospf retransmit-interval <3-65535>
+ ip ospf transmit-delay <1-65535>
+
+You can have these new parameters committed to the configuration file by
+typing:
+
+	<hostname>(config-if)# write file
+
+For further details of the above commands, please see the Quagga
+documentation:
+
+	http://www.quagga.net/docs.php
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/Solaris/exec_attr	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,2 @@
+Network Management:solaris:cmd:RO::/usr/sbin/quaggaadm:privs=basic
+Network Management:solaris:cmd:RO::/usr/sbin/zebraadm:privs=basic
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/Solaris/ospfd.HA.conf.sample	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,34 @@
+!
+! Zebra configuration saved from vty
+!
+# Specify the hostname of the server
+hostname Servername 
+!
+# Specify the password for the vty interface. This will be your telnet login
+# password 
+password zebra
+!
+# Specify the log file name
+log file /var/tmp/ospf.log
+!
+service advanced-vty
+!
+router ospf
+!
+# Specify the router-id to your machine's primary interface IP address
+ospf router-id a.b.c.d 
+!
+redistribute connected
+!
+# Specify the OSPF enabled interface(s).Enter a network entry per OSPF
+# enabled interface If the interface has an address
+# of 10.0.0.1/8 then the command below provides network information
+# to the ospf routers  :
+#                network 10.0.0.0/8 area 0
+# the network command's mask length should be the same as the interface
+# address's mask. All the network entries must have the same area number
+network a.b.c.d/m area  <0-4294967295>
+!
+line vty
+ exec-timeout 0 0
+!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/Solaris/quagga.1m	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,139 @@
+'\" te
+.\" Copyright (c) 2006, 2011, Oracle and/or its affiliates\&. All rights reserved\&.
+.TH quagga 1m "01 Aug 2006" "SunOS 5.11" "System Administration Commands"
+.SH "NAME"
+quagga - advanced routing protocol suite from www.quagga.net
+.SH "SYNOPSIS"
+.PP
+\fBQuagga\fR is an advanced routing software package that provides a suite of
+TCP/IP based routing protocols.
+.PP
+\fBQuagga\fR supports protocols such as RIPv1, RIPv2, RIPng, OSPFv2, OSPFv3,
+BGP-4, and BGP-4+. This document is provided for informational purposes to
+help users configure quagga under Solaris specifically. Further
+documentation on quagga configuration in general is provided at
+http://www.quagga.net.
+.SH "DESCRIPTION"
+.PP
+\fBQuagga\fR consists of the following package:
+.sp
+.in +2
+.ie t \(bu
+.el o
+.mk
+.in +3
+.rt
+system/network/routing/quagga - Quagga root files
+.sp
+.in -3
+.in -2
+.PP
+More detailed \fBQuagga\fR documentation is available from
+http://www.quagga.net/docs, or in the GNU Info format \fBQuagga\fR documentation
+installed in /usr/share/info.
+.PP
+In Solaris, quagga daemons are managed by the following smf(5) services:
+.PP
+svc:/network/routing/zebra:quagga	zebra routing manager daemon
+.PP
+svc:/network/routing/rip:quagga	RIPv2 routing daemon
+.PP
+svc:/network/routing/ripng:quagga	RIPng routing daemon
+.PP
+svc:/network/routing/bgp:quagga	BGP routing daemon
+.PP
+svc:/network/routing/ospf:quagga	OSPF routing daemon
+.PP
+svc:/network/routing/ospf6:quagga	OSPF6 routing daemon
+.PP
+\fBQuagga\fR daemons can be configured either via the \fBsmf\fR(5) framework, or via routeadm(1M),
+the global dynamic routing management utility.
+.PP
+Note that quagga was built without the vtysh (unified vty shell) utility, as it depends on a package
+not currently distributed with Solaris.
+.PP
+.SH "COMMANDS"
+.PP
+By default, \fBin.routed\fR and \fBin.ripngd\fR, managed by the svc:/network/routing/route
+and svc:/network/routing/ripng services respectively, are configured as default 
+IPv4/IPv6 routing services. To disable these, either utilize \fBrouteadm\fR (1M) or 
+\fBsvcadm\fR (1M) as follows:
+.sp
+.nf
+\f(CW# routeadm -d route
+# routeadm -d ripng:default
+\fR
+.fi
+.sp
+disables in.routed and in.ripngd respectively using \fBrouteadm\fR (1M). 
+.sp
+.nf
+\f(CW# svcadm disable route:default
+# svcadm disable ripng:default
+\fR
+.fi
+.sp
+disables in.routed and in.ripngd respectively using smf(1). 
+.PP
+Note that these services must be disabled or their quagga counterparts
+(rip:quagga, ripng:quagga) will not run, since they services specify to the
+smf framework that they and their quagga counterparts cannot run
+simultaneously.
+.PP
+To enable quagga services, again either \fBrouteadm\fR(1M) or \fBsvcadm\fR(1M) can be used.
+Note that quagga services, like in.routed and in.ripngd, specify dependencies
+on ipv4(6)-routing(forwarding) services, so as with all smf(1) services, ensure the 
+appropriate dependencies are enabled or the services will not start.
+.PP
+For example,
+.sp
+.nf
+\f(CW# routeadm -e rip:quagga
+# routeadm -e ipv4-routing -u
+\fR
+.fi
+.sp
+enables quagga's RIPv2 daemon (the -u option above is required, as 
+global ip routing/forwarding settings are not applied to the system until next reboot
+unless the -u is issued).
+.PP
+To do the same via \fBsmf\fR(1),
+.sp
+.nf
+\f(CW# svcadm enable -r rip:quagga
+\fR
+.fi
+.sp
+The "-r" option recursively enables rip:quagga's dependencies, which include
+the ipv4-routing service.
+.PP
+The status of all routing daemon services can be displayed via \fBrouteadm\fR(1M),
+run without options, or, through smf(1), via the svcs(1) command.
+.PP
+Commandline arguments to the quagga daemons can be altered by
+modifying the routing/daemon-args property, again either via \fBrouteadm\fR(1M)
+or svccfg(1M):
+.sp
+.nf
+\f(CW# routeadm -m rip:quagga daemon-args="-s"
+\fR
+.fi
+.sp
+or
+.sp
+.nf
+\f(CW# svccfg -s rip:quagga setprop routing/daemon-args="-s"
+# svcadm refresh rip:quagga
+\fR
+.fi
+.sp
+The "refresh" is required to ensure the daemon re-reads its configuration.
+.SH "DIAGNOSTICS"
+.PP
+To see status of all routing services, run \fBrouteadm\fR(1M) without options.
+.SH "SEE ALSO"
+.PP
+\fBripngd\fR(1m), \fBisisd\fR(1m), \fBospfd\fR(1m), \fBospf6d\fR(1m), \fBbgpd\fR(1m), \fBzebra\fR(1m), \fBrouteadm\fR(1M),
+\fBsvcadm\fR(1M), svcs(1), svccfg(1M), smf(5)
+.SH "AUTHORS"
+See <http://www.quagga.net>, or the Info file for an  accurate  list of authors.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/Solaris/quaggaadm	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,114 @@
+#!/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+#
+# ident	"@(#)quaggaadm	1.3	08/10/02 SMI"
+daemons="zebra ripd ripngd ospfd ospf6d bgpd isisd"
+
+function quaggaadm_usage
+{
+	print - "Usage:\n" >&2
+	for H in ${daemons} ; do
+		print - "\t${1} [-e] ${H}\t\tConnect to ${H} daemon" >&2
+	done
+	print - "\nThe -e flag enables the named daemon to accept ${1} connections and" >&2
+	print - "must be used on the first use of ${1} to connect to a particular daemon." >&2
+}
+
+if [ ${#} -gt 2 ]
+then
+	quaggaadm_usage ${0}
+	exit 1
+fi
+
+function vty_enable
+{
+	restart=0;
+	
+	/usr/bin/svcprop -p routing/vty_address ${1} \
+		| grep -- '[0-9a-zA-Z]' > /dev/null || \
+		/usr/sbin/routeadm -m ${1} vty_address="127.0.0.1" && \
+			restart=1
+	/usr/bin/svcprop -p routing/vty_port ${1}| grep 0 > /dev/null && \
+		/usr/sbin/routeadm -m ${1} vty_port=${2}
+	if [ $restart = "1" ]; then
+		/usr/sbin/svcadm restart ${1}
+	fi			
+}			
+
+ENABLE="no"
+
+if [ ${#} -eq 2 ] ; then
+	DAEMON=${2}
+	if [ ${1} != "-e" ]; then
+		quaggaadm_usage ${0}
+		exit 1;
+	fi
+	ENABLE="yes"
+elif [ ${#} -eq 1 ] ; then
+	DAEMON=${1}
+fi
+
+case ${DAEMON} in
+	zebra)    # telnet to zebra daemon
+		PORT=2601
+		SVC="zebra:quagga"
+	;;
+	rip|ripd) # telnet to ripd daemon
+		PORT=2602
+		SVC="rip:quagga"
+	;;
+	ripng|ripngd)    # telnet to ripngd daemon
+		PORT=2603
+		SVC="ripng:quagga"
+	;;
+	ospfd|ospf)    # telnet to ospfd daemon
+		PORT=2604
+		SVC="ospf:quagga"
+	;;
+	bgp|bgpd)    # telnet to bgpd daemon
+		PORT=2605
+		SVC="bgp:quagga"
+	;;
+	ospf6|ospf6d)    # telnet to ospf6d daemon
+		PORT=2606
+		SVC="ospf6:quagga"
+	;;
+	isis|isisd)	# telnet to isisd daemon
+		PORT=2608
+		SVC="isis:quagga"
+	;;
+	*)
+		# unknown daemon
+		print - "Unrecognized command: ${1}..." >&2
+		quaggaadm_usage ${0}
+		exit 1
+	;;
+esac
+
+if [ ${ENABLE} = "yes" ] ; then
+	vty_enable ${SVC} ${PORT} || exit 1
+fi
+
+exec telnet 127.0.0.1 ${PORT}
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/Solaris/quaggaadm.1m	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,63 @@
+'\" te
+.\" Copyright (c) 2008, 2011, Oracle and/or its affiliates\&. All rights reserved\&.
+.\" ident	"@(#)quaggaadm.1m	1.3	10/03/16 SMI"
+.TH quaggaadm 1M "02 Oct 2008" "" ""
+.SH "NAME"
+quaggaadm \- quagga routing daemon administration tool
+.SH "SYNOPSIS"
+.PP
+\fBquaggaadm\fR [-\fBe\fR] \fIzebra\fR|\fIripd\fR|\fIripngd\fR|\fIospfd\fR|\fIospf6d\fR|\fIbgpd\fR|\fIisisd\fR
+.SH "DESCRIPTION"
+.PP
+The \fBquaggaadm\fR command is provided as a simple wrapper to facilitate connection
+to the quagga daemons respective vty terminal telnet interfaces. \fBquaggaadm\fR simply telnets
+the appropriate vty port for the specified daemon on localhost, and from there the
+user can update configuration for that daemon. Note that if the vty is disabled
+for the given daemon, \fBquaggaadm\fR will prompt the user to request enable. Note
+that to complete this operation, the user will require solaris.smf.manage.routing
+and solaris.smf.modify.routing authorizations.
+.SH "OPTIONS"
+.PP
+The following command-line options are supported:
+.sp
+.ne 2
+.mk
+.na
+\fB\fB\-e\fR\fR
+.ad
+.sp .6
+.in +4
+Automatically enable the vty for the specified daemon.
+.sp
+.sp 1
+.in -4
+.SH "EXIT STATUS"
+.PP
+The following exit values are returned:
+.sp
+.ne 2
+.mk
+.na
+\fB\fB0\fR\fR
+.ad
+.sp .6
+.in +4
+Successful completion.
+.sp
+.sp 1
+.in -4
+.sp
+.ne 2
+.mk
+.na
+\fB\fB!=0\fR\fR
+.ad
+.sp .6
+.in +4
+An error occurred while obtaining or modifying the system configuration.
+.sp
+.sp 1
+.in -4
+.SH "SEE ALSO"
+.PP
+\fBquagga\fR(1M), \fBattributes\fR(5), \fBrouteadm\fR(1M)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/10-sunw-smf.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,983 @@
+--- solaris/quagga.init.in
++++ solaris/quagga.init.in
+@@ -1,6 +1,6 @@
+ #!/sbin/sh
+ #
+-# Copyright 2001,2003 Sun Microsystems, Inc. All rights reserved.
++# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+ # Use is subject to license terms.
+ #
+ # This file is part of Quagga.
+@@ -25,40 +25,88 @@
+ # Starts/stops the given daemon
+ 
+ SMFINCLUDE=/lib/svc/share/smf_include.sh
++ROUTEADMINCLUDE=/lib/svc/share/routing_include.sh
++GLOBAL_OPTIONS="PAfiug"
+ DAEMON_PATH=@sbindir@
++USER=@enable_user@
++GROUP=@enable_group@
+ 
++# Relevant to S10+
+ quagga_is_globalzone () {
+-	if [ "${QUAGGA_INIT_ZONENAME:=`/sbin/zonename`}" != "global" ]; then
+-		return 1
+-	else
++	if [ "${QUAGGA_INIT_ZONENAME:=`/sbin/zonename`}" = "global" \
++		-o `/sbin/zonename -t` = "exclusive" ]; then
+ 		return 0
++	else
++		return 1
+ 	fi
+ }
+ 
+-# Include smf functions, if available. If not, define smf_present to indicate
+-# there is no SMF. Should allow this script to work pre-S10.
+-if [ -f "$SMFINCLUDE" ] ; then
+-	. "$SMFINCLUDE";
+-else
+-	# pre-SMF system, fake up any functions and exit codes
+-	# which SMFINCLUDE usually provides.
+-	smf_present () {
+-		return 1
+-	}
+-	SMF_EXIT_OK=0;
+-	SMF_EXIT_ERR_CONFIG=96;
+-	SMF_EXIT_ERR_FATAL=95;
+-fi
++routeadm_daemon_args () {
++	# globals
++	args="`get_daemon_option_from_property $SMF_FMRI config_file f`"
++	args="${args} `get_daemon_option_from_property $SMF_FMRI vty_port P`"
++	args="${args} `get_daemon_option_from_property $SMF_FMRI vty_address A`"
++	args="${args} `get_daemon_option_from_property $SMF_FMRI pid_file i`"
+ 	
+-# if there's no SMF, set some default DAEMON_ARGS
+-smf_present || DAEMON_ARGS=""
++	# user and group we need for config file upgrade..
++	SMF_USER=`get_routeadm_property $SMF_FMRI user`
++	SMF_GROUP=`get_routeadm_property()$SMF_FMRI group`
++	if [ "${SMF_USER}" ] ; then
++		USER="${SMF_USER}"
++		args="${args} -u ${SMF_USER}"
++	fi
++	if [ "${SMF_GROUP}" ] ; then 
++		GROUP="${SMF_GROUP}"
++		args="${args} -g ${SMF_GROUP}"
++	fi
+ 
+-usage () {
+-	if smf_present ; then
+-		echo "Usage: $0 <daemon> <daemon arguments>";
+-	else
+-		echo "Usage: $0 <stop|start> <daemon> <daemon arguments>";
++	case $1 in
++	zebra)
++		args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI batch -b true`"
++		args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI retain -r true`"
++		;;
++	ripd|ripngd)
++		args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI retain -r true`"
++		;;
++	bgpd)
++		args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI retain -r true`"
++		args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI no_kernel -n true`"
++		args="${args} `get_daemon_option_from_property $SMF_FMRI bgp_port p 179`"
++		;;
++	esac
++	echo ${args}
++}
++
++# certain daemons need zebra
++routeadm_zebra_enable () {
++
++	if [ "$DAEMON" = "zebra" ]; then
++		return
+ 	fi
++	
++	enable_zebra=`/usr/bin/svcprop -p \
++		routing/enable_zebra $SMF_FMRI 2> /dev/null`
++	if [ "$enable_zebra" != "false" ]; then
++		zenabled=`/usr/bin/svcprop -p general/enabled zebra:quagga`
++		zenabledt=`/usr/bin/svcprop -p general_ovr/enabled zebra:quagga`
++		if [ "$zenabled" = "true" -o "$zenabledt" = "true" ]; then
++			/usr/sbin/svcadm disable zebra:quagga
++			/usr/sbin/svcadm enable -st zebra:quagga
++		else
++			/usr/sbin/svcadm enable -st zebra:quagga 
++		fi
++		if [ "$?" != "0" ]; then
++			echo "Could not enable zebra:quagga"
++			exit $SMF_EXIT_ERR_FATAL
++		fi
++	fi
++}
++
++. "$SMFINCLUDE";
++. "$ROUTEADMINCLUDE"
++	
++usage () {
++	echo "Usage: $0 <daemon>";
+ 	echo "The --pid_file argument is implied";
+ 	echo "This help message: $0 <help|usage>";
+ }
+@@ -67,20 +115,13 @@
+ case $1 in
+ 	'help' | 'usage')
+ 		usage
+-		exit SMF_EXIT_OK
++		exit $SMF_EXIT_OK
+ 		;;
+ esac
+ 
+-if smf_present ; then
+-	QUAGGA_METHOD="start"
+-else
+-	QUAGGA_METHOD="$1"
+-	shift;
+-fi
++QUAGGA_METHOD="start"
+ 
+ DAEMON="$1"
+-shift
+-DAEMON_ARGS="$@"
+ 
+ # daemon path must be given
+ if [ -z "$DAEMON_PATH/$DAEMON" ]; then
+@@ -91,12 +132,9 @@
+ # only bgpd is suitable for running in a non-global zone, at this
+ # time.
+ case "${DAEMON}" in
+-	zebra)
+-		quagga_is_globalzone || exit $SMF_EXIT_OK
+-	;;
+ 	bgpd)
+ 	;;
+-	ospfd | ospf6d | ripd | ripngd )
++	zebra | ospfd | ospf6d | ripd | ripngd )
+ 		quagga_is_globalzone || exit $SMF_EXIT_OK
+ 	;;
+ 	*)
+@@ -105,6 +143,14 @@
+ 	;;
+ esac
+ 
++DAEMON_ARGS=`routeadm_daemon_args $DAEMON`;
++routeadm_zebra_enable $DAEMON;
++
++if [ ! -f "@sysconfdir@/${DAEMON}.conf" ] ; then
++	echo "Could not find config file, @sysconfdir@/${DAEMON}.conf"
++	exit $SMF_EXIT_ERR_CONFIG
++fi
++
+ # we need @quagga_statedir@ to exist, it probably is on tmpfs.
+ if [ ! -d @quagga_statedir@ ] ; then
+ 	mkdir -p @quagga_statedir@
+@@ -115,7 +161,11 @@
+ PIDFILE="@quagga_statedir@/${DAEMON}.pid"
+ 
+ start () {
+-	$DAEMON_PATH/$DAEMON $DAEMON_ARGS --pid_file ${PIDFILE} &
++	if [ ! -x "$DAEMON_PATH/$DAEMON" ] ; then
++		echo "Error, could not find daemon, $DAEMON_PATH/$DAEMON"
++		exit $SMF_EXIT_ERR_FATAL
++	fi
++	eval exec $DAEMON_PATH/$DAEMON $DAEMON_ARGS --pid_file ${PIDFILE} &
+ }
+ 
+ stop () {
+@@ -134,7 +184,7 @@
+ 
+ *)
+ 	usage
+-	exit SMF_EXIT_ERR_FATAL
++	exit $SMF_EXIT_ERR_FATAL
+ 	;;
+ esac	
+ 
+--- solaris/quagga.xml.in
++++ solaris/quagga.xml.in
+@@ -18,18 +18,20 @@
+ 	Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 	02111-1307, USA.
+ 
+-	Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
++	Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ 	Use is subject to license terms.
+ 
+ 	ident	"@(#)quagga.xml	1.0	05/03/15 SMI"
+ -->
+ 
+-<service_bundle type='manifest' name='QUAGGAdaemons:quagga'>
++<service_bundle type='manifest' name='SUNWquagga-daemons:quagga'>
++
+ <service
+ 	name='network/routing/zebra'
+ 	type='service'
+ 	version='1'>
+-
++	
++	<single_instance />
+ 	<instance name='quagga' enabled='false'>
+ 	
+ 	<dependency name='fs'
+@@ -46,24 +48,24 @@
+ 		type='service'>
+ 		<service_fmri value='svc:/network/initial' />
+ 	</dependency>
+-
+-	<dependency name='config_data'
++	
++	<!-- do not not run unless routing-setup has run -->
++	<dependency
++		name='network_routing_setup'
+ 		grouping='require_all'
+-		restart_on='restart'
+-		type='path'>
+-		<service_fmri
+-			value='file://localhost/@sysconfdir@/zebra.conf' />
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/routing-setup' />
+ 	</dependency>
+-	
++
+ 	<exec_method
+ 		type='method'
+ 		name='start'
+-		exec='/lib/svc/method/quagga zebra %{routing/daemon-args}'
++		exec='/lib/svc/method/quagga zebra'
+ 		timeout_seconds='60'>
+ 		<method_context>
+ 		  <method_credential
+-		   user='root' group='root'
+-		   privileges='basic,net_icmpaccess,net_rawaccess,sys_admin,sys_net_config'/>
++		   user='root' group='root'/>
+  		</method_context>
+ 	</exec_method>
+ 
+@@ -84,11 +86,48 @@
+ 		<propval name='ignore_error'
+ 		    type='astring' value='core,signal' />
+ 	</property_group>
+-
++	
++	<!-- Properties in this group are used by routeadm (1M) -->
++	<property_group name='routeadm' type='application'>
++		<stability value='Unstable' />
++		<!-- Identifies service as a routing service -->
++		<propval name='daemon' type='astring'
++			value='@sbindir@/zebra' />
++		<propval name='value_authorization' type='astring'
++			value='solaris.smf.value.routing' />
++		<!-- zebra should not contribute to ipv4/ipv6 routing state -->
++		<propval name='protocol' type='astring' value='zebra' />
++	</property_group>
++	
++	<!-- Properties in this group are modifiable via routeadm (1M) -->
+ 	<property_group name='routing' type='application'>
++		<stability value='Evolving' />
+ 		<propval name='value_authorization' type='astring' 
+-		         value='solaris.smf.modify.routing' />
+-		<propval name='daemon-args' type='astring' value='-P 0'/>
++		         value='solaris.smf.value.routing' />
++		
++		<!-- Options common to Quagga daemons
++		     Property names are equivalent to the long
++		     option name, consult Quagga documentation -->
++		<!-- The config file to use, if not the default -->
++		<propval name='config_file' type='astring' value=''/>
++		<!-- The vty_port to listen on if not the default.
++		     0 to disable --> 
++		<propval name='vty_port' type='integer' value='0' />
++		<!-- The address to bind the VTY interface to, if not any. -->
++		<propval name='vty_address' type='astring' value='' />
++		<!-- The user to switch to after startup, if not the default -->
++		<propval name='user' type='astring' value='' />
++		<!-- The group to switch to, if not the default.
++		     If user is specified, this defaults to a group with
++		     same name as user -->
++		<propval name='group' type='astring' value='' />
++		<!-- The pidfile to use, if not the default of
++		     @quagga_statedir@ -->
++		<propval name='pid_file' type='astring' value='' />
++		
++		<!-- Options specific to zebra -->
++		<propval name='batch' type='boolean' value='false' />
++		<propval name='retain' type='boolean' value='false' />
+ 	</property_group>
+ 	
+ 	<property_group name='general' type='framework'>
+@@ -95,6 +134,8 @@
+ 		<!-- to start stop routing services -->
+ 		<propval name='action_authorization' type='astring'
+ 			 value='solaris.smf.manage.routing' />
++		<propval name='value_authorization' type='astring'
++			 value='solaris.smf.manage.routing' />
+ 	</property_group>
+ 
+ 	<template>
+@@ -111,7 +152,7 @@
+ 		</documentation>
+ 	</template>
+ 	</instance>
+-	<stability value='Evolving' />
++	<stability value='Unstable' />
+ </service>
+ 
+ <service
+@@ -129,39 +170,40 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 
+-	<!-- Depends on Work-In-Progress, not yet in SNV
+-	<dependency name='net'
+-		grouping='require_all'
+-		restart_on='none'
++	<dependency
++		name='ipv4-forwarding'
++		grouping='optional_all'
++		restart_on='refresh'
+ 		type='service'>
+-		<service_fmri value='svc:/network/routing/ipv4-routing' />
++		<service_fmri value='svc:/network/ipv4-forwarding' />
+ 	</dependency>
+-	-->
+ 	
+-	<dependency name='zebra'
+-			grouping='require_all'
+-			restart_on='restart'
+-			type='service'>
+-			<service_fmri value='svc:/network/routing/zebra:quagga' />
+-	</dependency>
+-
+-	<dependency name='config_data'
++	<!-- do not not run unless routing-setup has run -->
++	<dependency
++		name='network_routing_setup'
+ 		grouping='require_all'
+-		restart_on='restart'
+-		type='path'>
+-		<service_fmri
+-			value='file://localhost/@sysconfdir@/ripd.conf' />
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/routing-setup' />
+ 	</dependency>
+ 	
++	<!-- ensure that restart of zebra is propogated to daemon -->
++	<dependency
++		name='zebra'
++		grouping='optional_all'
++		restart_on='restart'
++		type='service'>
++		<service_fmri value='svc:/network/routing/zebra:quagga' />
++	</dependency>
++
+ 	<exec_method
+ 		type='method'
+ 		name='start'
+-		exec='/lib/svc/method/quagga ripd %{routing/daemon-args}'
++		exec='/lib/svc/method/quagga ripd'
+ 		timeout_seconds='60'>
+ 		<method_context>
+ 		  <method_credential
+-		   user='root' group='root'
+-		   privileges='basic,net_icmpaccess,net_privaddr,net_rawaccess'/>
++		   user='root' group='root'/>
+  		</method_context>
+ 	</exec_method>
+ 
+@@ -179,10 +221,42 @@
+ 		    type='astring' value='core,signal' />
+ 	</property_group>
+ 
++	<!-- Properties in this group are used by routeadm (1M) -->
++	<property_group name='routeadm' type='application'>
++		<stability value='Unstable' />
++		<!-- Identifies service as a routing service -->
++		<propval name='daemon' type='astring'
++			value='@sbindir@/ripd' />
++		<propval name='value_authorization' type='astring'
++			value='solaris.smf.value.routing' />
++		<propval name='protocol' type='astring' value='ipv4' />
++	</property_group>
++	
++	<!-- Properties in this group are modifiable via routeadm (1M) -->
+ 	<property_group name='routing' type='application'>
+ 		<propval name='value_authorization' type='astring' 
+-		         value='solaris.smf.modify.routing' />
+-		<propval name='daemon-args' type='astring' value='-P 0'/>
++		         value='solaris.smf.value.routing' />
++
++		<!-- Options common to Quagga daemons -->
++		<!-- The config file to use, if not the default -->
++		<propval name='config_file' type='astring' value=''/>
++		<!-- The vty_port to listen on if not the default.
++		     0 to disable --> 
++		<propval name='vty_port' type='integer' value='0' />
++		<!-- The address to bind the VTY interface to, if not any. -->
++		<propval name='vty_address' type='astring' value='' />
++		<!-- The user to switch to after startup, if not the default -->
++		<propval name='user' type='astring' value='' />
++		<!-- The group to switch to, if not the default.
++		     If user is specified, this defaults to a group with
++		     same name as user -->
++		<propval name='group' type='astring' value='' />
++		<!-- The pidfile to use, if not the default of
++		     @quagga_statedir@ -->
++		<propval name='pid_file' type='astring' value='' />
++		
++		<!-- Options specific to ripd -->
++		<propval name='retain' type='boolean' value='false' />
+ 	</property_group>
+ 
+ 	<property_group name='general' type='framework'>
+@@ -189,6 +263,8 @@
+ 		<!-- to start stop routing services -->
+ 		<propval name='action_authorization' type='astring'
+ 			 value='solaris.smf.manage.routing' />
++		<propval name='value_authorization' type='astring'
++			 value='solaris.smf.manage.routing' />
+ 	</property_group>
+ 
+ 	<template>
+@@ -205,7 +281,7 @@
+ 		</documentation>
+ 	</template>
+ 	</instance>
+-	<stability value='Evolving' />
++	<stability value='Unstable' />
+ </service>
+ 
+ <service
+@@ -223,39 +299,40 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 	
+-	<!-- Depends on WIP, not yet in SNV
+-	<dependency name='net'
+-		grouping='require_all'
+-		restart_on='none'
++	<dependency
++		name='ipv6-forwarding'
++		grouping='optional_all'
++		restart_on='refresh'
+ 		type='service'>
+-		<service_fmri value='svc:/network/routing/ipv6-routing' />
++		<service_fmri value='svc:/network/ipv6-forwarding' />
+ 	</dependency>
+-	-->
+ 
+-	<dependency name='zebra'
+-			grouping='require_all'
+-			restart_on='restart'
+-			type='service'>
+-			<service_fmri value='svc:/network/routing/zebra:quagga' />
++	<!-- do not not run unless routing-setup has run -->
++	<dependency
++		name='network_routing_setup'
++		grouping='require_all'
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/routing-setup' />
+ 	</dependency>
+ 
+-	<dependency name='config_data'
+-		grouping='require_all'
++	<!-- ensure that restart of zebra is propogated to daemon -->
++	<dependency
++		name='zebra'
++		grouping='optional_all'
+ 		restart_on='restart'
+-		type='path'>
+-		<service_fmri
+-			value='file://localhost/@sysconfdir@/ripngd.conf' />
++		type='service'>
++		<service_fmri value='svc:/network/routing/zebra:quagga' />
+ 	</dependency>
+-	
++
+ 	<exec_method
+ 		type='method'
+ 		name='start'
+-		exec='/lib/svc/method/quagga ripngd %{routing/daemon-args}'
++		exec='/lib/svc/method/quagga ripngd'
+ 		timeout_seconds='60'>
+ 		<method_context>
+ 		  <method_credential
+-		   user='root' group='root'
+-		   privileges='basic,net_icmpaccess,net_privaddr,net_rawaccess'/>
++		   user='root' group='root'/>
+  		</method_context>
+ 	</exec_method>
+ 
+@@ -273,10 +350,42 @@
+ 		    type='astring' value='core,signal' />
+ 	</property_group>
+ 
++	<!-- Properties in this group are used by routeadm (1M) -->
++	<property_group name='routeadm' type='application'>
++		<stability value='Unstable' />
++		<!-- Identifies service as a routing service -->
++		<propval name='daemon' type='astring'
++			value='@sbindir@/ripngd' />
++		<propval name='value_authorization' type='astring'
++			value='solaris.smf.value.routing' />
++		<propval name='protocol' type='astring' value='ipv6'/>
++	</property_group>
++	
++	<!-- Properties in this group are modifiable via routeadm (1M) -->
+ 	<property_group name='routing' type='application'>
+ 		<propval name='value_authorization' type='astring' 
+-		         value='solaris.smf.modify.routing' />
+-		<propval name='daemon-args' type='astring' value='-P 0'/>
++		         value='solaris.smf.value.routing' />
++
++		<!-- Options common to Quagga daemons -->
++		<!-- The config file to use, if not the default -->
++		<propval name='config_file' type='astring' value=''/>
++		<!-- The vty_port to listen on if not the default.
++		     0 to disable --> 
++		<propval name='vty_port' type='integer' value='0' />
++		<!-- The address to bind the VTY interface to, if not any. -->
++		<propval name='vty_address' type='astring' value='' />
++		<!-- The user to switch to after startup, if not the default -->
++		<propval name='user' type='astring' value='' />
++		<!-- The group to switch to, if not the default.
++		     If user is specified, this defaults to a group with
++		     same name as user -->
++		<propval name='group' type='astring' value='' />
++		<!-- The pidfile to use, if not the default of
++		     @quagga_statedir@ -->
++		<propval name='pid_file' type='astring' value='' />
++
++		<!-- Options specific to ripngd -->
++		<propval name='retain' type='boolean' value='false' />
+ 	</property_group>
+ 
+ 	<property_group name='general' type='framework'>
+@@ -283,6 +392,8 @@
+ 		<!-- to start stop routing services -->
+ 		<propval name='action_authorization' type='astring'
+ 			 value='solaris.smf.manage.routing' />
++		<propval name='value_authorization' type='astring'
++			 value='solaris.smf.manage.routing' />
+ 	</property_group>
+ 
+ 	<template>
+@@ -299,7 +410,7 @@
+ 		</documentation>
+ 	</template>
+ 	</instance>
+-	<stability value='Evolving' />
++	<stability value='Unstable' />
+ </service>
+ 
+ <service
+@@ -317,47 +428,51 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 	
+-	<!-- Depends on WIP, not yet in SNV
+-	<dependency name='net'
+-		grouping='require_all'
+-		restart_on='none'
++	<dependency
++		name='ipv4-forwarding'
++		grouping='optional_all'
++		restart_on='refresh'
+ 		type='service'>
+-		<service_fmri value='svc:/network/routing/ipv4-routing' />
++		<service_fmri value='svc:/network/ipv4-forwarding' />
+ 	</dependency>
+-	-->
+ 
+-	<dependency name='zebra'
+-			grouping='require_all'
+-			restart_on='restart'
+-			type='service'>
+-			<service_fmri value='svc:/network/routing/zebra:quagga' />
++	<!-- do not not run unless routing-setup has run -->
++	<dependency
++		name='network_routing_setup'
++		grouping='require_all'
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/routing-setup' />
+ 	</dependency>
+ 
+-	<dependency name='config_data'
+-		grouping='require_all'
++	<!-- ensure that restart of zebra is propogated to daemon -->
++	<dependency
++		name='zebra'
++		grouping='optional_all'
+ 		restart_on='restart'
+-		type='path'>
+-		<service_fmri
+-			value='file://localhost/@sysconfdir@/ospfd.conf' />
++		type='service'>
++		<service_fmri value='svc:/network/routing/zebra:quagga' />
+ 	</dependency>
+-	
++
+ 	<exec_method
+ 		type='method'
+ 		name='start'
+-		exec='/lib/svc/method/quagga ospfd %{routing/daemon-args}'
++		exec='/lib/svc/method/quagga ospfd'
+ 		timeout_seconds='60'>
+ 		<method_context>
+ 		  <method_credential
+-		   user='root' group='root'
+-		   privileges='basic,net_icmpaccess,net_privaddr,net_rawaccess,sys_net_config'/>
++		   user='root' group='root'/>
+  		</method_context>
+ 	</exec_method>
+ 
++	<!-- ospfd can take a long time to shutdown, due to graceful 
++	     shutdown 
++	 -->
+ 	<exec_method
+ 		type='method'
+ 		name='stop'
+ 		exec=':kill'
+-		timeout_seconds='60'>
++		timeout_seconds='600'>
+ 	</exec_method>
+ 
+ 	<property_group name='startd'
+@@ -367,10 +482,39 @@
+ 		    type='astring' value='core,signal' />
+ 	</property_group>
+ 
++	<!-- Properties in this group are used by routeadm (1M) -->
++	<property_group name='routeadm' type='application'>
++		<stability value='Unstable' />
++		<!-- Identifies service as a routing service -->
++		<propval name='daemon' type='astring'
++			value='@sbindir@/ospfd' />
++		<propval name='value_authorization' type='astring'
++			value='solaris.smf.value.routing' />
++		<propval name='protocol' type='astring' value='ipv4'/>
++	</property_group>
++	
++	<!-- Properties in this group are modifiable via routeadm (1M) -->
+ 	<property_group name='routing' type='application'>
+ 		<propval name='value_authorization' type='astring' 
+-		         value='solaris.smf.modify.routing' />
+-		<propval name='daemon-args' type='astring' value='-P 0'/>
++		         value='solaris.smf.value.routing' />
++
++		<!-- Options common to Quagga daemons -->
++		<!-- The config file to use, if not the default -->
++		<propval name='config_file' type='astring' value=''/>
++		<!-- The vty_port to listen on if not the default.
++		     0 to disable --> 
++		<propval name='vty_port' type='integer' value='0' />
++		<!-- The address to bind the VTY interface to, if not any. -->
++		<propval name='vty_address' type='astring' value='' />
++		<!-- The user to switch to after startup, if not the default -->
++		<propval name='user' type='astring' value='' />
++		<!-- The group to switch to, if not the default.
++		     If user is specified, this defaults to a group with
++		     same name as user -->
++		<propval name='group' type='astring' value='' />
++		<!-- The pidfile to use, if not the default of
++		     @quagga_statedir@ -->
++		<propval name='pid_file' type='astring' value='' />
+ 	</property_group>
+ 
+ 	<property_group name='general' type='framework'>
+@@ -377,6 +521,8 @@
+ 		<!-- to start stop routing services -->
+ 		<propval name='action_authorization' type='astring'
+ 			 value='solaris.smf.manage.routing' />
++		<propval name='value_authorization' type='astring'
++			 value='solaris.smf.manage.routing' />
+ 	</property_group>
+ 
+ 	<template>
+@@ -393,7 +539,7 @@
+ 		</documentation>
+ 	</template>
+ 	</instance>
+-	<stability value='Evolving' />
++	<stability value='Unstable' />
+ </service>
+ 
+ <service
+@@ -411,39 +557,40 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 	
+-	<!-- Depends on WIP, not yet in SNV
+-	<dependency name='net'
+-		grouping='require_all'
+-		restart_on='none'
++	<dependency
++		name='ipv6-forwarding'
++		grouping='optional_all'
++		restart_on='refresh'
+ 		type='service'>
+-		<service_fmri value='svc:/network/routing/ipv6-routing' />
++		<service_fmri value='svc:/network/ipv6-forwarding' />
+ 	</dependency>
+-	-->
+ 
+-	<dependency name='zebra'
+-			grouping='require_all'
+-			restart_on='restart'
+-			type='service'>
+-			<service_fmri value='svc:/network/routing/zebra:quagga' />
++	<!-- do not not run unless routing-setup has run -->
++	<dependency
++		name='network_routing_setup'
++		grouping='require_all'
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/routing-setup' />
+ 	</dependency>
+ 
+-	<dependency name='config_data'
+-		grouping='require_all'
++	<!-- ensure that restart of zebra is propogated to daemon -->
++	<dependency
++		name='zebra'
++		grouping='optional_all'
+ 		restart_on='restart'
+-		type='path'>
+-		<service_fmri
+-			value='file://localhost/@sysconfdir@/ospf6d.conf' />
++		type='service'>
++		<service_fmri value='svc:/network/routing/zebra:quagga' />
+ 	</dependency>
+-	
++
+ 	<exec_method
+ 		type='method'
+ 		name='start'
+-		exec='/lib/svc/method/quagga ospf6d %{routing/daemon-args}'
++		exec='/lib/svc/method/quagga ospf6d'
+ 		timeout_seconds='60'>
+ 		<method_context>
+ 		  <method_credential
+-		   user='root' group='root'
+-		   privileges='basic,net_icmpaccess,net_privaddr,net_rawaccess'/>
++		   user='root' group='root'/>
+  		</method_context>
+ 	</exec_method>
+ 
+@@ -461,10 +608,39 @@
+ 		    type='astring' value='core,signal' />
+ 	</property_group>
+ 
++	<!-- Properties in this group are used by routeadm (1M) -->
++	<property_group name='routeadm' type='application'>
++		<stability value='Unstable' />
++		<!-- Identifies service as a routing service -->
++		<propval name='daemon' type='astring'
++			value='@sbindir@/ospf6d' />
++		<propval name='value_authorization' type='astring'
++			value='solaris.smf.value.routing' />
++		<propval name='protocol' type='astring' value='ipv6'/>
++	</property_group>
++	
++	<!-- Properties in this group are modifiable via routeadm (1M) -->
+ 	<property_group name='routing' type='application'>
+ 		<propval name='value_authorization' type='astring' 
+-		         value='solaris.smf.modify.routing' />
+-		<propval name='daemon-args' type='astring' value='-P 0'/>
++		         value='solaris.smf.value.routing' />
++
++		<!-- Options common to Quagga daemons -->
++		<!-- The config file to use, if not the default -->
++		<propval name='config_file' type='astring' value=''/>
++		<!-- The vty_port to listen on if not the default.
++		     0 to disable --> 
++		<propval name='vty_port' type='integer' value='0' />
++		<!-- The address to bind the VTY interface to, if not any. -->
++		<propval name='vty_address' type='astring' value='' />
++		<!-- The user to switch to after startup, if not the default -->
++		<propval name='user' type='astring' value='' />
++		<!-- The group to switch to, if not the default.
++		     If user is specified, this defaults to a group with
++		     same name as user -->
++		<propval name='group' type='astring' value='' />
++		<!-- The pidfile to use, if not the default of
++		     @quagga_statedir@ -->
++		<propval name='pid_file' type='astring' value='' />
+ 	</property_group>
+ 
+ 	<property_group name='general' type='framework'>
+@@ -471,12 +647,14 @@
+ 		<!-- to start stop routing services -->
+ 		<propval name='action_authorization' type='astring'
+ 			 value='solaris.smf.manage.routing' />
++		<propval name='value_authorization' type='astring'
++			 value='solaris.smf.manage.routing' />
+ 	</property_group>
+ 
+ 	<template>
+ 		<common_name>
+ 			<loctext xml:lang='C'>
+-			Quagga: ospf6d, OSPFv3 IPv6 routing protocol daemon.
++ 			Quagga: ospf6d, OSPFv3 IPv6 routing protocol daemon.
+ 			</loctext>
+ 		</common_name>
+ 		<documentation>
+@@ -487,7 +665,7 @@
+ 		</documentation>
+ 	</template>
+ 	</instance>
+-	<stability value='Evolving' />
++	<stability value='Unstable' />
+ </service>
+ 
+ 
+@@ -506,40 +684,48 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 	
+-	<!-- Depends on WIP, not yet in SNV
+-	<dependency name='net'
+-		grouping='require_any'
+-		restart_on='none'
++	<dependency
++		name='ipv6-forwarding'
++		grouping='optional_all'
++		restart_on='refresh'
+ 		type='service'>
+-		<service_fmri value='svc:/network/routing/ipv6-routing' />
+-		<service_fmri value='svc:/network/routing/ipv4-routing' />
++		<service_fmri value='svc:/network/ipv6-forwarding' />
+ 	</dependency>
+-	-->
++
++	<dependency
++		name='ipv4-forwarding'
++		grouping='optional_all'
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/ipv4-forwarding' />
++	</dependency>
+ 	
+-	<dependency name='zebra'
+-			grouping='optional_all'
+-			restart_on='restart'
+-			type='service'>
+-			<service_fmri value='svc:/network/routing/zebra:quagga' />
++	<!-- do not not run unless routing-setup has run -->
++	<dependency
++		name='network_routing_setup'
++		grouping='require_all'
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/routing-setup' />
+ 	</dependency>
+ 
+-	<dependency name='config_data'
+-		grouping='require_all'
++	<!-- ensure that restart of zebra is propogated to daemon -->
++	<dependency
++		name='zebra'
++		grouping='optional_all'
+ 		restart_on='restart'
+-		type='path'>
+-		<service_fmri
+-			value='file://localhost/@sysconfdir@/bgpd.conf' />
++		type='service'>
++		<service_fmri value='svc:/network/routing/zebra:quagga' />
+ 	</dependency>
+-	
++
+ 	<exec_method
+ 		type='method'
+ 		name='start'
+-		exec='/lib/svc/method/quagga bgpd %{routing/daemon-args}'
++		exec='/lib/svc/method/quagga bgpd'
+ 		timeout_seconds='60'>
+ 		<method_context>
+ 		  <method_credential
+-		   user='root' group='root'
+-		   privileges='basic,net_icmpaccess,net_privaddr,net_rawaccess'/>
++		   user='root' group='root'/>
+  		</method_context>
+ 	</exec_method>
+ 
+@@ -557,10 +743,55 @@
+ 		    type='astring' value='core,signal' />
+ 	</property_group>
+ 
++	<!-- Properties in this group are used by routeadm (1M) -->
++	<property_group name='routeadm' type='application'>
++		<stability value='Unstable' />
++		<!-- Identifies service as a routing service -->
++		<propval name='daemon' type='astring'
++			value='@sbindir@/bgpd' />
++		<propval name='value_authorization' type='astring'
++			value='solaris.smf.value.routing' />
++		<property name='protocol' type='astring'>
++			<astring_list>
++				<value_node value='ipv4'/>
++				<value_node value='ipv6'/>
++			</astring_list>
++		</property>
++	</property_group>
++	
++	<!-- Properties in this group are modifiable via routeadm (1M) -->
+ 	<property_group name='routing' type='application'>
+ 		<propval name='value_authorization' type='astring' 
+-		         value='solaris.smf.modify.routing' />
+-		<propval name='daemon-args' type='astring' value='-P 0'/>
++		         value='solaris.smf.value.routing' />
++
++		<!-- Options common to Quagga daemons. -->
++		<!-- The config file to use, if not the default -->
++		<propval name='config_file' type='astring' value=''/>
++		<!-- The vty_port to listen on if not the default.
++		     0 to disable --> 
++		<propval name='vty_port' type='integer' value='0' />
++		<!-- The address to bind the VTY interface to, if not any. -->
++		<propval name='vty_address' type='astring' value='' />
++		<!-- The user to switch to after startup, if not the default -->
++		<propval name='user' type='astring' value='' />
++		<!-- The group to switch to, if not the default.
++		     If user is specified, this defaults to a group with
++		     same name as user -->
++		<propval name='group' type='astring' value='' />
++		<!-- The pidfile to use, if not the default of
++		     @quagga_statedir@ -->
++		<propval name='pid_file' type='astring' value='' />
++
++		<!-- Options specific to bgpd -->
++		<propval name='retain' type='boolean' value='false' />
++		<propval name='no_kernel' type='boolean' value='false' />
++		<propval name='bgp_port' type='astring' value='' />
++		
++		<!--
++			If enable_zebra is false, it will not be switched
++			on by the start method.
++		-->
++		<propval name='enable_zebra' type='boolean' value='true' />
+ 	</property_group>
+ 
+ 	<property_group name='general' type='framework'>
+@@ -567,6 +798,8 @@
+ 		<!-- to start stop routing services -->
+ 		<propval name='action_authorization' type='astring'
+ 			 value='solaris.smf.manage.routing' />
++		<propval name='value_authorization' type='astring'
++			 value='solaris.smf.manage.routing' />
+ 	</property_group>
+ 
+ 	<template>
+@@ -583,6 +816,6 @@
+ 		</documentation>
+ 	</template>
+ 	</instance>
+-	<stability value='Evolving' />
++	<stability value='Unstable' />
+ </service>
+ </service_bundle>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/15-privs-ipinst.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,15 @@
+--- lib/privs.c
++++ lib/privs.c
+@@ -117,7 +117,12 @@
+   /* Quagga -> Solaris privilege mappings */
+   [ZCAP_SETID] =	{ 1, (pvalue_t []) { PRIV_PROC_SETID		}, },
+   [ZCAP_BIND] = 	{ 1, (pvalue_t []) { PRIV_NET_PRIVADDR		}, },
++  /* IP_CONFIG is a subset of NET_CONFIG and is allowed in zones */
++#ifdef PRIV_SYS_IP_CONFIG
++  [ZCAP_NET_ADMIN] =	{ 1, (pvalue_t []) { PRIV_SYS_IP_CONFIG	}, },
++#else
+   [ZCAP_NET_ADMIN] =	{ 1, (pvalue_t []) { PRIV_SYS_NET_CONFIG	}, },
++#endif
+   [ZCAP_NET_RAW] = 	{ 2, (pvalue_t []) { PRIV_NET_RAWACCESS,
+                                              PRIV_NET_ICMPACCESS 	}, },
+   [ZCAP_CHROOT] = 	{ 1, (pvalue_t []) { PRIV_PROC_CHROOT		}, },
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/20-privs-bgpd.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,10 @@
+--- bgpd/bgp_main.c
++++ bgpd/bgp_main.c
+@@ -103,7 +103,6 @@
+ zebra_capabilities_t _caps_p [] =  
+ {
+     ZCAP_BIND, 
+-    ZCAP_NET_RAW,
+ };
+ 
+ struct zebra_privs_t bgpd_privs =
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/25-isisd-dlpi.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,2333 @@
+diff --git a/ChangeLog b/ChangeLog
+index 38ea66d..ad1c40c 100644
+--- ChangeLog
++++ ChangeLog
+@@ -1,3 +1,8 @@
++2007-08-07 James Carlson <[email protected]>
++
++	* configure.ac: Added support for separate link-layer access
++	  mechanisms in isisd.
++
+ 2007-07-27 Paul Jakma <[email protected]>
+ 
+ 	* configure.ac: Bump version to 0.99.8
+diff --git a/configure.ac b/configure.ac
+index 857f415..437767e 100755
+--- configure.ac
++++ configure.ac
+@@ -750,6 +750,35 @@
+ AC_SUBST(KERNEL_METHOD)
+ AC_SUBST(OTHER_METHOD)
+ 
++dnl --------------------------
++dnl Determine IS-IS I/O method
++dnl --------------------------
++AC_CHECK_HEADER(net/bpf.h)
++AC_CHECK_HEADER(sys/dlpi.h)
++AC_MSG_CHECKING(zebra IS-IS I/O method)
++if test x"$opsys" = x"gnu-linux"; then
++  AC_MSG_RESULT(pfpacket)
++  ISIS_METHOD=isis_pfpacket.o
++elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
++  AC_MSG_RESULT(DLPI)
++  ISIS_METHOD="isis_dlpi.o"
++else
++  if test $ac_cv_header_net_bpf_h = no; then
++    if test $ac_cv_header_sys_dlpi_h = no; then
++      AC_MSG_RESULT(none)
++      AC_MSG_WARN([*** IS-IS support will not be built ***])
++      ISISD=""
++    else
++      AC_MSG_RESULT(DLPI)
++    fi
++    ISIS_METHOD="isis_dlpi.o"
++  else
++    AC_MSG_RESULT(BPF)
++    ISIS_METHOD="isis_bpf.o"
++  fi
++fi
++AC_SUBST(ISIS_METHOD)
++
+ dnl ------------------------------------
+ dnl check for broken CMSG_FIRSTHDR macro
+ dnl ------------------------------------
+diff --git a/isisd/ChangeLog b/isisd/ChangeLog
+index 8797af1..c2482f0 100644
+--- isisd/ChangeLog
++++ isisd/ChangeLog
+@@ -1,3 +1,20 @@
++2008-01-29 James Carlson <[email protected]>
++
++	* Fix bug #437, assert due to bogus index management 
++	* isis_flags.c: (flags_initialize) new
++	* (flags_get_index) fix off by one, leading to list assert
++	  on null node data.
++	  (flags_free_index) ditto.
++	* isisd.c: (isis_area_create) use flags_initialize
++	  (isis_area_destroy) deconfigure circuits when
++	  taking down area.
++
++2007-07-18 James Carlson <[email protected]>
++
++	* isis_network.c: split up into isis_bpf.c, isis_dlpi.c, and
++	  isis_pfpacket.c, selected by autoconf, and added DLPI support.
++	* (general) Fixed to allow compilation and use on Solaris.
++
+ 2006-12-08 Hannes Gredler <[email protected]>
+ 
+ 	* isis_adjacency.c: (isis_new_adj) Allow NULL snpa argument.
+diff --git a/isisd/Makefile.am b/isisd/Makefile.am
+index 1dd5493..859facd 100644
+--- isisd/Makefile.am
++++ isisd/Makefile.am
+@@ -9,9 +9,11 @@ noinst_LIBRARIES = libisis.a
+ sbin_PROGRAMS = isisd 
+ SUBDIRS = topology
+ 
++isis_method = @ISIS_METHOD@
++
+ libisis_a_SOURCES = \
+ 	isis_adjacency.c isis_lsp.c dict.c isis_circuit.c isis_pdu.c \
+-	isis_tlv.c isisd.c isis_misc.c isis_network.c isis_zebra.c isis_dr.c \
++	isis_tlv.c isisd.c isis_misc.c isis_zebra.c isis_dr.c \
+ 	isis_flags.c isis_dynhn.c iso_checksum.c isis_csm.c isis_events.c \
+ 	isis_spf.c isis_route.c isis_routemap.c
+ 
+@@ -26,7 +28,11 @@ noinst_HEADERS = \
+ isisd_SOURCES = \
+ 	isis_main.c $(libisis_a_SOURCES)
+ 
+-isisd_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
++isisd_LDADD = $(isis_method) @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
++
++isisd_DEPENDENCIES = $(isis_method)
++
++EXTRA_DIST = isis_bpf.c isis_dlpi.c isis_pfpacket.c
+ 
+ examplesdir = $(exampledir)
+ dist_examples_DATA = isisd.conf.sample
+diff --git a/isisd/dict.c b/isisd/dict.c
+index a333d3e..6c3e1e7 100644
+--- isisd/dict.c
++++ isisd/dict.c
+@@ -14,12 +14,13 @@
+  * into proprietary software; there is no requirement for such software to
+  * contain a copyright notice related to this source.
+  *
+- * $Id: dict.c,v 1.4 2005/09/25 12:04:25 hasso Exp $
+- * $Name:  $
++ * $Id$
++ * $Name$
+  */
+ 
+ #include <stdlib.h>
+ #include <stddef.h>
++#include "zebra.h"
+ #include "zassert.h"
+ #define DICT_IMPLEMENTATION
+ #include "dict.h"
+diff --git a/isisd/include-netbsd/iso.h b/isisd/include-netbsd/iso.h
+index 714b42d..1a80aec 100644
+--- isisd/include-netbsd/iso.h
++++ isisd/include-netbsd/iso.h
+@@ -192,7 +192,13 @@ extern struct protosw isosw[];
+ #else
+ /* user utilities definitions from the iso library */
+ 
++#ifdef SUNOS_5
++#define	__P(x)	x
++#define	__BEGIN_DECLS
++#define	__END_DECLS
++#else
+ #include <sys/cdefs.h>
++#endif
+ 
+ __BEGIN_DECLS
+ struct iso_addr *iso_addr __P((const char *));
+diff --git a/isisd/isisd.c b/isisd/isisd.c
+index 714b42d..1a80aec 100644
+--- isisd/isisd.c
++++ isisd/isisd.c
+@@ -130,7 +130,7 @@
+   area->circuit_list = list_new ();
+   area->area_addrs = list_new ();
+   THREAD_TIMER_ON (master, area->t_tick, lsp_tick, area, 1);
+-  area->flags.maxindex = -1;
++  flags_initialize (&area->flags);
+   /*
+    * Default values
+    */
+@@ -215,7 +215,11 @@
+   if (area->circuit_list)
+     {
+       for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit))
+-        isis_circuit_del (circuit);
++	{
++	  /* The fact that it's in circuit_list means that it was configured */
++	  isis_circuit_deconfigure (circuit, area);
++	  isis_circuit_del (circuit);
++	}
+       
+       list_delete (area->circuit_list);
+     }
+diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c
+new file mode 100644
+index 0000000..e66ac98
+--- /dev/null
++++ isisd/isis_bpf.c
+@@ -0,0 +1,340 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_bpf.c
++ *
++ * Copyright (C) 2001,2002    Sampo Saaristo
++ *                            Tampere University of Technology      
++ *                            Institute of Communications Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#include <zebra.h>
++#include <net/if.h>
++#include <netinet/if_ether.h>
++#include <sys/time.h>
++#include <sys/ioctl.h>
++#include <net/bpf.h>
++
++#include "log.h"
++#include "stream.h"
++#include "if.h"
++
++#include "isisd/dict.h"
++#include "isisd/include-netbsd/iso.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_common.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_flags.h"
++#include "isisd/isisd.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_network.h"
++
++#include "privs.h"
++
++extern struct zebra_privs_t isisd_privs;
++
++struct bpf_insn llcfilter[] = {
++  BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN),	/* check first byte */
++  BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5),
++  BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1),
++  BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 3),	/* check second byte */
++  BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2),
++  BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1),	/* check third byte */
++  BPF_STMT (BPF_RET + BPF_K, (u_int) - 1),
++  BPF_STMT (BPF_RET + BPF_K, 0)
++};
++int readblen = 0;
++u_char *readbuff = NULL;
++
++/*
++ * Table 9 - Architectural constants for use with ISO 8802 subnetworks
++ * ISO 10589 - 8.4.8
++ */
++
++u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
++u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
++u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
++u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
++
++static char sock_buff[8192];
++
++static int
++open_bpf_dev (struct isis_circuit *circuit)
++{
++  int i = 0, fd;
++  char bpfdev[128];
++  struct ifreq ifr;
++  u_int16_t blen;
++  int true = 1, false = 0;
++  struct timeval timeout;
++  struct bpf_program bpf_prog;
++
++  do
++    {
++      (void) snprintf (bpfdev, sizeof (bpfdev), "/dev/bpf%d", i++);
++      fd = open (bpfdev, O_RDWR);
++    }
++  while (fd < 0 && errno == EBUSY);
++
++  if (fd < 0)
++    {
++      zlog_warn ("open_bpf_dev(): failed to create bpf socket: %s",
++		 safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  zlog_debug ("Opened BPF device %s", bpfdev);
++
++  memcpy (ifr.ifr_name, circuit->interface->name, sizeof (ifr.ifr_name));
++  if (ioctl (fd, BIOCSETIF, (caddr_t) & ifr) < 0)
++    {
++      zlog_warn ("open_bpf_dev(): failed to bind to interface: %s",
++		 safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  if (ioctl (fd, BIOCGBLEN, (caddr_t) & blen) < 0)
++    {
++      zlog_warn ("failed to get BPF buffer len");
++      blen = circuit->interface->mtu;
++    }
++
++  readblen = blen;
++
++  if (readbuff == NULL)
++    readbuff = malloc (blen);
++
++  zlog_debug ("BPF buffer len = %u", blen);
++
++  /*  BPF(4): reads return immediately upon packet reception.
++   *  Otherwise, a read will block until either the kernel
++   *  buffer becomes full or a timeout occurs. 
++   */
++  if (ioctl (fd, BIOCIMMEDIATE, (caddr_t) & true) < 0)
++    {
++      zlog_warn ("failed to set BPF dev to immediate mode");
++    }
++
++#ifdef BIOCSSEESENT
++  /*
++   * We want to see only incoming packets
++   */
++  if (ioctl (fd, BIOCSSEESENT, (caddr_t) & false) < 0)
++    {
++      zlog_warn ("failed to set BPF dev to incoming only mode");
++    }
++#endif
++
++  /*
++   * ...but all of them
++   */
++  if (ioctl (fd, BIOCPROMISC, (caddr_t) & true) < 0)
++    {
++      zlog_warn ("failed to set BPF dev to promiscuous mode");
++    }
++
++  /*
++   * If the buffer length is smaller than our mtu, lets try to increase it
++   */
++  if (blen < circuit->interface->mtu)
++    {
++      if (ioctl (fd, BIOCSBLEN, &circuit->interface->mtu) < 0)
++	{
++	  zlog_warn ("failed to set BPF buffer len (%u to %u)", blen,
++		     circuit->interface->mtu);
++	}
++    }
++
++  /*
++   * Set a timeout parameter - hope this helps select()
++   */
++  timeout.tv_sec = 600;
++  timeout.tv_usec = 0;
++  if (ioctl (fd, BIOCSRTIMEOUT, (caddr_t) & timeout) < 0)
++    {
++      zlog_warn ("failed to set BPF device timeout");
++    }
++
++  /*
++   * And set the filter
++   */
++  memset (&bpf_prog, 0, sizeof (struct bpf_program));
++  bpf_prog.bf_len = 8;
++  bpf_prog.bf_insns = &(llcfilter[0]);
++  if (ioctl (fd, BIOCSETF, (caddr_t) & bpf_prog) < 0)
++    {
++      zlog_warn ("open_bpf_dev(): failed to install filter: %s",
++		 safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  assert (fd > 0);
++
++  circuit->fd = fd;
++
++  return ISIS_OK;
++}
++
++/*
++ * Create the socket and set the tx/rx funcs
++ */
++int
++isis_sock_init (struct isis_circuit *circuit)
++{
++  int retval = ISIS_OK;
++
++  if (isisd_privs.change (ZPRIVS_RAISE))
++    zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
++
++  retval = open_bpf_dev (circuit);
++
++  if (retval != ISIS_OK)
++    {
++      zlog_warn ("%s: could not initialize the socket", __func__);
++      goto end;
++    }
++
++  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
++    {
++      circuit->tx = isis_send_pdu_bcast;
++      circuit->rx = isis_recv_pdu_bcast;
++    }
++  else if (circuit->circ_type == CIRCUIT_T_P2P)
++    {
++      circuit->tx = isis_send_pdu_p2p;
++      circuit->rx = isis_recv_pdu_p2p;
++    }
++  else
++    {
++      zlog_warn ("isis_sock_init(): unknown circuit type");
++      retval = ISIS_WARNING;
++      goto end;
++    }
++
++end:
++  if (isisd_privs.change (ZPRIVS_LOWER))
++    zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
++
++  return retval;
++}
++
++int
++isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
++{
++  int bytesread = 0, bytestoread, offset, one = 1;
++  struct bpf_hdr *bpf_hdr;
++
++  assert (circuit->fd > 0);
++
++  if (ioctl (circuit->fd, FIONREAD, (caddr_t) & bytestoread) < 0)
++    {
++      zlog_warn ("ioctl() FIONREAD failed: %s", safe_strerror (errno));
++    }
++
++  if (bytestoread)
++    {
++      bytesread = read (circuit->fd, readbuff, readblen);
++    }
++  if (bytesread < 0)
++    {
++      zlog_warn ("isis_recv_pdu_bcast(): read() failed: %s",
++		 safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  if (bytesread == 0)
++    return ISIS_WARNING;
++
++  bpf_hdr = (struct bpf_hdr *) readbuff;
++
++  assert (bpf_hdr->bh_caplen == bpf_hdr->bh_datalen);
++
++  offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN;
++
++  /* then we lose the BPF, LLC and ethernet headers */
++  stream_write (circuit->rcv_stream, readbuff + offset, 
++                bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN);
++  stream_set_getp (circuit->rcv_stream, 0);
++
++  memcpy (ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN,
++	  ETHER_ADDR_LEN);
++
++  if (ioctl (circuit->fd, BIOCFLUSH, &one) < 0)
++    zlog_warn ("Flushing failed: %s", safe_strerror (errno));
++
++  return ISIS_OK;
++}
++
++int
++isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
++{
++  int bytesread;
++
++  bytesread = stream_read (circuit->rcv_stream, circuit->fd, 
++                           circuit->interface->mtu);
++
++  if (bytesread < 0)
++    {
++      zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  return ISIS_OK;
++}
++
++int
++isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
++{
++  struct ether_header *eth;
++  int written;
++
++  stream_set_getp (circuit->snd_stream, 0);
++
++  /*
++   * First the eth header
++   */
++  eth = (struct ether_header *) sock_buff;
++  if (level == 1)
++    memcpy (eth->ether_dhost, ALL_L1_ISS, ETHER_ADDR_LEN);
++  else
++    memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN);
++  memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN);
++  eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
++
++  /*
++   * Then the LLC
++   */
++  sock_buff[ETHER_HDR_LEN] = ISO_SAP;
++  sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP;
++  sock_buff[ETHER_HDR_LEN + 2] = 0x03;
++
++  /* then we copy the data */
++  memcpy (sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data,
++	  stream_get_endp (circuit->snd_stream));
++
++  /* now we can send this */
++  written = write (circuit->fd, sock_buff,
++		   stream_get_endp (circuit->snd_stream) 
++		    + LLC_LEN + ETHER_HDR_LEN);
++
++  return ISIS_OK;
++}
++
++int
++isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
++{
++  return ISIS_OK;
++}
+diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
+index fe3eb82..af24988 100644
+--- isisd/isis_circuit.c
++++ isisd/isis_circuit.c
+@@ -26,6 +26,10 @@
+ #include <netinet/if_ether.h>
+ #endif
+ 
++#ifndef ETHER_ADDR_LEN
++#define	ETHER_ADDR_LEN	ETHERADDRL
++#endif
++
+ #include "log.h"
+ #include "memory.h"
+ #include "if.h"
+@@ -381,11 +385,13 @@ isis_circuit_if_add (struct isis_circuit *circuit, struct interface *ifp)
+        * Get the Hardware Address
+        */
+ #ifdef HAVE_STRUCT_SOCKADDR_DL
++#ifndef SUNOS_5
+       if (circuit->interface->sdl.sdl_alen != ETHER_ADDR_LEN)
+ 	zlog_warn ("unsupported link layer");
+       else
+ 	memcpy (circuit->u.bc.snpa, LLADDR (&circuit->interface->sdl),
+ 		ETH_ALEN);
++#endif
+ #else
+       if (circuit->interface->hw_addr_len != ETH_ALEN)
+ 	{
+@@ -447,10 +453,12 @@ isis_circuit_update_params (struct isis_circuit *circuit,
+    * Get the Hardware Address
+    */
+ #ifdef HAVE_STRUCT_SOCKADDR_DL
++#ifndef SUNOS_5
+   if (circuit->interface->sdl.sdl_alen != ETHER_ADDR_LEN)
+     zlog_warn ("unsupported link layer");
+   else
+     memcpy (circuit->u.bc.snpa, LLADDR (&circuit->interface->sdl), ETH_ALEN);
++#endif
+ #else
+   if (circuit->interface->hw_addr_len != ETH_ALEN)
+     {
+diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
+index b5ef269..a7e719f 100644
+--- isisd/isis_circuit.h
++++ isisd/isis_circuit.h
+@@ -69,6 +69,7 @@ struct isis_circuit
+   struct isis_area *area;	/* back pointer to the area */
+   struct interface *interface;	/* interface info from z */
+   int fd;			/* IS-IS l1/2 socket */
++  int sap_length;		/* SAP length for DLPI */
+   struct nlpids nlpids;
+   /*
+    * Threads
+diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c
+new file mode 100644
+index 0000000..db4383b
+--- /dev/null
++++ isisd/isis_dlpi.c
+@@ -0,0 +1,607 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_dlpi.c
++ *
++ * Copyright (C) 2001,2002    Sampo Saaristo
++ *                            Tampere University of Technology      
++ *                            Institute of Communications Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#include <zebra.h>
++#include <net/if.h>
++#include <netinet/if_ether.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <stropts.h>
++#include <poll.h>
++#include <sys/dlpi.h>
++#include <sys/pfmod.h>
++
++#include "log.h"
++#include "stream.h"
++#include "if.h"
++
++#include "isisd/dict.h"
++#include "isisd/include-netbsd/iso.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_common.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_flags.h"
++#include "isisd/isisd.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_network.h"
++
++#include "privs.h"
++
++extern struct zebra_privs_t isisd_privs;
++
++static t_uscalar_t dlpi_ctl[1024];	/* DLPI control messages */
++
++/*
++ * Table 9 - Architectural constants for use with ISO 8802 subnetworks
++ * ISO 10589 - 8.4.8
++ */
++
++u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
++u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
++u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
++u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
++
++static u_char sock_buff[8192];
++
++static u_short pf_filter[] =
++{
++  ENF_PUSHWORD + 0,		/* Get the SSAP/DSAP values */
++  ENF_PUSHLIT | ENF_CAND,	/* Check them */
++  ISO_SAP | (ISO_SAP << 8),
++  ENF_PUSHWORD + 1,		/* Get the control value */
++  ENF_PUSHLIT | ENF_AND,	/* Isolate it */
++#ifdef _BIG_ENDIAN
++  0xFF00,
++#else
++  0x00FF,
++#endif
++  ENF_PUSHLIT | ENF_CAND,	/* Test for expected value */
++#ifdef _BIG_ENDIAN
++  0x0300
++#else
++  0x0003
++#endif
++};
++
++/*
++ * We would like to use something like libdlpi here, but that's not present on
++ * all versions of Solaris or on any non-Solaris system, so it's nowhere near
++ * as portable as we'd like.  Thus, we use the standards-conformant DLPI
++ * interfaces plus the (optional; not needed) Solaris packet filter module.
++ */
++
++static void
++dlpisend (int fd, const void *cbuf, size_t cbuflen,
++  const void *dbuf, size_t dbuflen, int flags)
++{
++  const struct strbuf *ctlptr = NULL;
++  const struct strbuf *dataptr = NULL;
++  struct strbuf ctlbuf, databuf;
++
++  if (cbuf != NULL)
++    {
++      memset (&ctlbuf, 0, sizeof (ctlbuf));
++      ctlbuf.len = cbuflen;
++      ctlbuf.buf = (void *)cbuf;
++      ctlptr = &ctlbuf;
++    }
++
++  if (dbuf != NULL)
++    {
++      memset (&databuf, 0, sizeof (databuf));
++      databuf.len = dbuflen;
++      databuf.buf = (void *)dbuf;
++      dataptr = &databuf;
++    }
++
++  /* We assume this doesn't happen often and isn't operationally significant */
++  if (putmsg (fd, ctlptr, dataptr, flags) == -1)
++    zlog_debug ("%s: putmsg: %s", __func__, safe_strerror (errno));
++}
++
++static ssize_t
++dlpirctl (int fd)
++{
++  struct pollfd fds[1];
++  struct strbuf ctlbuf, databuf;
++  int flags, retv;
++
++  do
++    {
++      /* Poll is used here in case the device doesn't speak DLPI correctly */
++      memset (fds, 0, sizeof (fds));
++      fds[0].fd = fd;
++      fds[0].events = POLLIN | POLLPRI;
++      if (poll (fds, 1, 1000) <= 0)
++	return -1;
++
++      memset (&ctlbuf, 0, sizeof (ctlbuf));
++      memset (&databuf, 0, sizeof (databuf));
++      ctlbuf.maxlen = sizeof (dlpi_ctl);
++      ctlbuf.buf = (void *)dlpi_ctl;
++      databuf.maxlen = sizeof (sock_buff);
++      databuf.buf = (void *)sock_buff;
++      flags = 0;
++      retv = getmsg (fd, &ctlbuf, &databuf, &flags);
++
++      if (retv < 0)
++	return -1;
++    }
++  while (ctlbuf.len == 0);
++
++  if (!(retv & MORECTL))
++    {
++      while (retv & MOREDATA)
++	{
++	  flags = 0;
++	  retv = getmsg (fd, NULL, &databuf, &flags);
++	}
++      return ctlbuf.len;
++    }
++
++  while (retv & MORECTL)
++    {
++      flags = 0;
++      retv = getmsg (fd, &ctlbuf, &databuf, &flags);
++    }
++  return -1;
++}
++
++static int
++dlpiok (int fd, t_uscalar_t oprim)
++{
++  int retv;
++  dl_ok_ack_t *doa = (dl_ok_ack_t *)dlpi_ctl;
++
++  retv = dlpirctl (fd);
++  if (retv < DL_OK_ACK_SIZE || doa->dl_primitive != DL_OK_ACK ||
++    doa->dl_correct_primitive != oprim)
++    {
++      return -1;
++    }
++  else
++    {
++      return 0;
++    }
++}
++
++static int
++dlpiinfo (int fd)
++{
++  dl_info_req_t dir;
++  ssize_t retv;
++
++  memset (&dir, 0, sizeof (dir));
++  dir.dl_primitive = DL_INFO_REQ;
++  /* Info_req uses M_PCPROTO. */
++  dlpisend (fd, &dir, sizeof (dir), NULL, 0, RS_HIPRI);
++  retv = dlpirctl (fd);
++  if (retv < DL_INFO_ACK_SIZE || dlpi_ctl[0] != DL_INFO_ACK)
++    return -1;
++  else
++    return retv;
++}
++
++static int
++dlpiopen (const char *devpath, ssize_t *acklen)
++{
++  int fd, flags;
++
++  fd = open (devpath, O_RDWR | O_NONBLOCK | O_NOCTTY);
++  if (fd == -1)
++    return -1;
++
++  /* All that we want is for the open itself to be non-blocking, not I/O. */
++  flags = fcntl (fd, F_GETFL, 0);
++  if (flags != -1)
++    fcntl (fd, F_SETFL, flags & ~O_NONBLOCK);
++
++  /* After opening, ask for information */
++  if ((*acklen = dlpiinfo (fd)) == -1)
++    {
++      close (fd);
++      return -1;
++    }
++
++  return fd;
++}
++
++static int
++dlpiattach (int fd, int unit)
++{
++  dl_attach_req_t dar;
++
++  memset (&dar, 0, sizeof (dar));
++  dar.dl_primitive = DL_ATTACH_REQ;
++  dar.dl_ppa = unit;
++  dlpisend (fd, &dar, sizeof (dar), NULL, 0, 0);
++  return dlpiok (fd, dar.dl_primitive);
++}
++
++static int
++dlpibind (int fd)
++{
++  dl_bind_req_t dbr;
++  int retv;
++  dl_bind_ack_t *dba = (dl_bind_ack_t *)dlpi_ctl;
++
++  memset (&dbr, 0, sizeof (dbr));
++  dbr.dl_primitive = DL_BIND_REQ;
++  dbr.dl_service_mode = DL_CLDLS;
++  dlpisend (fd, &dbr, sizeof (dbr), NULL, 0, 0);
++
++  retv = dlpirctl (fd);
++  if (retv < DL_BIND_ACK_SIZE || dba->dl_primitive != DL_BIND_ACK)
++    return -1;
++  else
++    return 0;
++}
++
++static int
++dlpimcast (int fd, const u_char *mcaddr)
++{
++  struct {
++    dl_enabmulti_req_t der;
++    u_char addr[ETHERADDRL];
++  } dler;
++
++  memset (&dler, 0, sizeof (dler));
++  dler.der.dl_primitive = DL_ENABMULTI_REQ;
++  dler.der.dl_addr_length = sizeof (dler.addr);
++  dler.der.dl_addr_offset = dler.addr - (u_char *)&dler;
++  memcpy (dler.addr, mcaddr, sizeof (dler.addr));
++  dlpisend (fd, &dler, sizeof (dler), NULL, 0, 0);
++  return dlpiok (fd, dler.der.dl_primitive);
++}
++
++static int
++dlpiaddr (int fd, u_char *addr)
++{
++  dl_phys_addr_req_t dpar;
++  dl_phys_addr_ack_t *dpaa = (dl_phys_addr_ack_t *)dlpi_ctl;
++  int retv;
++
++  memset (&dpar, 0, sizeof (dpar));
++  dpar.dl_primitive = DL_PHYS_ADDR_REQ;
++  dpar.dl_addr_type = DL_CURR_PHYS_ADDR;
++  dlpisend (fd, &dpar, sizeof (dpar), NULL, 0, 0);
++
++  retv = dlpirctl (fd);
++  if (retv < DL_PHYS_ADDR_ACK_SIZE || dpaa->dl_primitive != DL_PHYS_ADDR_ACK)
++    return -1;
++
++  if (dpaa->dl_addr_offset < DL_PHYS_ADDR_ACK_SIZE ||
++    dpaa->dl_addr_length != ETHERADDRL ||
++    dpaa->dl_addr_offset + dpaa->dl_addr_length > retv)
++    return -1;
++
++  bcopy((char *)dpaa + dpaa->dl_addr_offset, addr, ETHERADDRL);
++  return 0;
++}
++
++static int
++open_dlpi_dev (struct isis_circuit *circuit)
++{
++  int fd, unit, retval;
++  char devpath[MAXPATHLEN];
++  dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl;
++  ssize_t acklen;
++
++  /* Only broadcast-type are supported at the moment */
++  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
++    {
++      zlog_warn ("%s: non-broadcast interface %s", __func__,
++	circuit->interface->name);
++      return ISIS_WARNING;
++    }
++
++  /* Try first as Style 1 */
++  (void) snprintf(devpath, sizeof (devpath), "/dev/%s",
++    circuit->interface->name);
++  unit = -1;
++  fd = dlpiopen (devpath, &acklen);
++
++  /* If that fails, try again as Style 2 */
++  if (fd == -1)
++    {
++      char *cp;
++
++      cp = devpath + strlen (devpath);
++      while (--cp >= devpath && isdigit(*cp))
++	;
++      unit = strtol(cp, NULL, 0);
++      *cp = '\0';
++      fd = dlpiopen (devpath, &acklen);
++
++      /* If that too fails, then the device really doesn't exist */
++      if (fd == -1)
++	{
++	  zlog_warn ("%s: unknown interface %s", __func__,
++	    circuit->interface->name);
++	  return ISIS_WARNING;
++	}
++
++      /* Double check the DLPI style */
++      if (dia->dl_provider_style != DL_STYLE2)
++	{
++	  zlog_warn ("open_dlpi_dev(): interface %s: %s is not style 2",
++	    circuit->interface->name, devpath);
++	  close (fd);
++	  return ISIS_WARNING;
++	}
++
++      /* If it succeeds, then we need to attach to the unit specified */
++      dlpiattach (fd, unit);
++
++      /* Reget the information, as it may be different per node */
++      if ((acklen = dlpiinfo (fd)) == -1)
++	{
++	  close (fd);
++	  return ISIS_WARNING;
++	}
++    }
++  else
++    {
++      /* Double check the DLPI style */
++      if (dia->dl_provider_style != DL_STYLE1)
++	{
++	  zlog_warn ("open_dlpi_dev(): interface %s: %s is not style 1",
++	    circuit->interface->name, devpath);
++	  close (fd);
++	  return ISIS_WARNING;
++	}
++    }
++
++  /* Check that the interface we've got is the kind we expect */
++  if ((dia->dl_sap_length != 2 && dia->dl_sap_length != -2) ||
++    dia->dl_service_mode != DL_CLDLS || dia->dl_addr_length != ETHERADDRL + 2 ||
++    dia->dl_brdcst_addr_length != ETHERADDRL)
++    {
++      zlog_warn ("%s: unsupported interface type for %s", __func__,
++	circuit->interface->name);
++      close (fd);
++      return ISIS_WARNING;
++    }
++  switch (dia->dl_mac_type)
++    {
++    case DL_CSMACD:
++    case DL_ETHER:
++    case DL_100VG:
++    case DL_100VGTPR:
++    case DL_ETH_CSMA:
++    case DL_100BT:
++      break;
++    default:
++      zlog_warn ("%s: unexpected mac type on %s: %d", __func__,
++	circuit->interface->name, dia->dl_mac_type);
++      close (fd);
++      return ISIS_WARNING;
++    }
++
++  circuit->sap_length = dia->dl_sap_length;
++
++  /*
++   * The local hardware address is something that should be provided by way of
++   * sockaddr_dl for the interface, but isn't on Solaris.  We set it here based
++   * on DLPI's reported address to avoid roto-tilling the world.
++   * (Note that isis_circuit_if_add on Solaris doesn't set the snpa.)
++   *
++   * Unfortunately, GLD is broken and doesn't provide the address after attach,
++   * so we need to be careful and use DL_PHYS_ADDR_REQ instead.
++   */
++  if (dlpiaddr (fd, circuit->u.bc.snpa) == -1)
++    {
++      zlog_warn ("open_dlpi_dev(): interface %s: unable to get MAC address",
++	circuit->interface->name);
++      close (fd);
++      return ISIS_WARNING;
++    }
++
++  /* Now bind to SAP 0.  This gives us 802-type traffic. */
++  if (dlpibind (fd) == -1)
++    {
++      zlog_warn ("%s: cannot bind SAP 0 on %s", __func__,
++	circuit->interface->name);
++      close (fd);
++      return ISIS_WARNING;
++    }
++
++  /*
++   * Join to multicast groups according to
++   * 8.4.2 - Broadcast subnetwork IIH PDUs
++   */
++  retval = 0;
++  if (circuit->circuit_is_type & IS_LEVEL_1)
++    {
++      retval |= dlpimcast (fd, ALL_L1_ISS);
++      retval |= dlpimcast (fd, ALL_ISS);
++    }
++  if (circuit->circuit_is_type & IS_LEVEL_2)
++    retval |= dlpimcast (fd, ALL_L2_ISS);
++
++  if (retval != 0)
++    {
++      zlog_warn ("%s: unable to join multicast on %s", __func__,
++	circuit->interface->name);
++      close (fd);
++      return ISIS_WARNING;
++    }
++
++  /* Push on the packet filter to avoid stray 802 packets */
++  if (ioctl (fd, I_PUSH, "pfmod") == 0)
++    {
++      struct packetfilt pfil;
++
++      pfil.Pf_Priority = 0;
++      pfil.Pf_FilterLen = sizeof (pf_filter) / sizeof (u_short);
++      memcpy (pfil.Pf_Filter, pf_filter, sizeof (pf_filter));
++      ioctl (fd, PFIOCSETF, &pfil);
++    }
++
++  circuit->fd = fd;
++
++  return ISIS_OK;
++}
++
++/*
++ * Create the socket and set the tx/rx funcs
++ */
++int
++isis_sock_init (struct isis_circuit *circuit)
++{
++  int retval = ISIS_OK;
++
++  if (isisd_privs.change (ZPRIVS_RAISE))
++    zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
++
++  retval = open_dlpi_dev (circuit);
++
++  if (retval != ISIS_OK)
++    {
++      zlog_warn ("%s: could not initialize the socket", __func__);
++      goto end;
++    }
++
++  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
++    {
++      circuit->tx = isis_send_pdu_bcast;
++      circuit->rx = isis_recv_pdu_bcast;
++    }
++  else
++    {
++      zlog_warn ("isis_sock_init(): unknown circuit type");
++      retval = ISIS_WARNING;
++      goto end;
++    }
++
++end:
++  if (isisd_privs.change (ZPRIVS_LOWER))
++    zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
++
++  return retval;
++}
++
++int
++isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
++{
++  struct pollfd fds[1];
++  struct strbuf ctlbuf, databuf;
++  int flags, retv;
++  dl_unitdata_ind_t *dui = (dl_unitdata_ind_t *)dlpi_ctl;
++
++  memset (fds, 0, sizeof (fds));
++  fds[0].fd = circuit->fd;
++  fds[0].events = POLLIN | POLLPRI;
++  if (poll (fds, 1, 0) <= 0)
++    return ISIS_WARNING;
++
++  memset (&ctlbuf, 0, sizeof (ctlbuf));
++  memset (&databuf, 0, sizeof (databuf));
++  ctlbuf.maxlen = sizeof (dlpi_ctl);
++  ctlbuf.buf = (void *)dlpi_ctl;
++  databuf.maxlen = sizeof (sock_buff);
++  databuf.buf = (void *)sock_buff;
++  flags = 0;
++  retv = getmsg (circuit->fd, &ctlbuf, &databuf, &flags);
++
++  if (retv < 0)
++    {
++      zlog_warn ("isis_recv_pdu_bcast: getmsg failed: %s",
++		 safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  if (retv & (MORECTL | MOREDATA))
++    {
++      while (retv & (MORECTL | MOREDATA))
++	{
++	  flags = 0;
++	  retv = getmsg (circuit->fd, &ctlbuf, &databuf, &flags);
++	}
++      return ISIS_WARNING;
++    }
++
++  if (ctlbuf.len < DL_UNITDATA_IND_SIZE ||
++    dui->dl_primitive != DL_UNITDATA_IND)
++    return ISIS_WARNING;
++
++  if (dui->dl_src_addr_length != ETHERADDRL + 2 ||
++    dui->dl_src_addr_offset < DL_UNITDATA_IND_SIZE ||
++    dui->dl_src_addr_offset + dui->dl_src_addr_length > ctlbuf.len)
++    return ISIS_WARNING;
++
++  memcpy (ssnpa, (char *)dui + dui->dl_src_addr_offset +
++    (circuit->sap_length > 0 ? circuit->sap_length : 0), ETHERADDRL);
++
++  if (databuf.len < LLC_LEN || sock_buff[0] != ISO_SAP ||
++    sock_buff[1] != ISO_SAP || sock_buff[2] != 3)
++    return ISIS_WARNING;
++
++  stream_write (circuit->rcv_stream, sock_buff + LLC_LEN,
++                databuf.len - LLC_LEN);
++  stream_set_getp (circuit->rcv_stream, 0);
++
++  return ISIS_OK;
++}
++
++int
++isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
++{
++  dl_unitdata_req_t *dur = (dl_unitdata_req_t *)dlpi_ctl;
++  char *dstaddr;
++  u_short *dstsap;
++
++  stream_set_getp (circuit->snd_stream, 0);
++
++  memset (dur, 0, sizeof (*dur));
++  dur->dl_primitive = DL_UNITDATA_REQ;
++  dur->dl_dest_addr_length = ETHERADDRL + 2;
++  dur->dl_dest_addr_offset = sizeof (*dur);
++
++  dstaddr = (char *)(dur + 1);
++  if (circuit->sap_length < 0)
++    {
++      dstsap = (u_short *)(dstaddr + ETHERADDRL);
++    }
++  else
++    {
++      dstsap = (u_short *)dstaddr;
++      dstaddr += circuit->sap_length;
++    }
++  if (level == 1)
++    memcpy (dstaddr, ALL_L1_ISS, ETHERADDRL);
++  else
++    memcpy (dstaddr, ALL_L2_ISS, ETHERADDRL);
++  /* Note: DLPI SAP values are in host byte order */
++  *dstsap = stream_get_endp (circuit->snd_stream) + LLC_LEN;
++
++  sock_buff[0] = ISO_SAP;
++  sock_buff[1] = ISO_SAP;
++  sock_buff[2] = 0x03;
++  memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data,
++	  stream_get_endp (circuit->snd_stream));
++  dlpisend (circuit->fd, dur, sizeof (*dur) + dur->dl_dest_addr_length,
++    sock_buff, stream_get_endp (circuit->snd_stream) + LLC_LEN, 0);
++  return ISIS_OK;
++}
+diff --git a/isisd/isis_flags.c b/isisd/isis_flags.c
+index 1dd5493..859facd 100644
+--- isisd/isis_flags.c
++++ isisd/isis_flags.c
+@@ -29,6 +29,13 @@
+ #include "isisd/isis_common.h"
+ #include "isisd/isis_flags.h"
+ 
++void
++flags_initialize (struct flags *flags)
++{
++  flags->maxindex = 0;
++  flags->free_idcs = NULL;
++}
++
+ int
+ flags_get_index (struct flags *flags)
+ {
+@@ -37,8 +44,7 @@
+ 
+   if (flags->free_idcs == NULL || flags->free_idcs->count == 0)
+     {
+-      flags->maxindex++;
+-      index = flags->maxindex;
++      index = flags->maxindex++;
+     }
+   else
+     {
+@@ -45,6 +51,7 @@
+       node = listhead (flags->free_idcs);
+       index = (int) listgetdata (node);
+       listnode_delete (flags->free_idcs, (void *) index);
++      index--;
+     }
+ 
+   return index;
+@@ -53,12 +60,18 @@
+ void
+ flags_free_index (struct flags *flags, int index)
+ {
++  if (index + 1 == flags->maxindex)
++    {
++      flags->maxindex--;
++      return;
++    }
++
+   if (flags->free_idcs == NULL)
+     {
+       flags->free_idcs = list_new ();
+     }
+ 
+-  listnode_add (flags->free_idcs, (void *) index);
++  listnode_add (flags->free_idcs, (void *) (index + 1));
+ 
+   return;
+ }
+diff --git a/isisd/isis_flags.h b/isisd/isis_flags.h
+--- isisd/isis_flags.h
++++ isisd/isis_flags.h
+index 1dd5493..859facd 100644
+@@ -28,6 +28,7 @@
+  * the support will be achived using the newest drafts */
+ #define ISIS_MAX_CIRCUITS 32 /* = 1024 */	/*FIXME:defined in lsp.h as well */
+ 
++void flags_initialize (struct flags *flags);
+ struct flags *new_flags (int size);
+ int flags_get_index (struct flags *flags);
+ void flags_free_index (struct flags *flags, int index);
+diff --git a/isisd/isis_network.c b/isisd/isis_network.c
+deleted file mode 100644
+index 56459ec..0000000
+--- isisd/isis_network.c
++++ /dev/null
+@@ -1,643 +0,0 @@
+-/*
+- * IS-IS Rout(e)ing protocol - isis_network.c   
+- *
+- * Copyright (C) 2001,2002    Sampo Saaristo
+- *                            Tampere University of Technology      
+- *                            Institute of Communications Engineering
+- *
+- * This program is free software; you can redistribute it and/or modify it 
+- * under the terms of the GNU General Public Licenseas published by the Free 
+- * Software Foundation; either version 2 of the License, or (at your option) 
+- * any later version.
+- *
+- * This program is distributed in the hope that it will be useful,but WITHOUT 
+- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+- * more details.
+-
+- * You should have received a copy of the GNU General Public License along 
+- * with this program; if not, write to the Free Software Foundation, Inc., 
+- * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+- */
+-
+-#include <zebra.h>
+-#ifdef GNU_LINUX
+-#include <net/ethernet.h>	/* the L2 protocols */
+-#else
+-#include <net/if.h>
+-#include <netinet/if_ether.h>
+-#endif
+-
+-#include "log.h"
+-#include "stream.h"
+-#include "if.h"
+-
+-#include "isisd/dict.h"
+-#include "isisd/include-netbsd/iso.h"
+-#include "isisd/isis_constants.h"
+-#include "isisd/isis_common.h"
+-#include "isisd/isis_circuit.h"
+-#include "isisd/isis_flags.h"
+-#include "isisd/isisd.h"
+-#include "isisd/isis_constants.h"
+-#include "isisd/isis_circuit.h"
+-#include "isisd/isis_network.h"
+-
+-#include "privs.h"
+-
+-extern struct zebra_privs_t isisd_privs;
+-
+-/*
+- * On linux we can use the packet(7) sockets, in other OSs we have to do with
+- * Berkley Packet Filter (BPF). Please tell me if you can think of a better 
+- * way...
+- */
+-#ifdef GNU_LINUX
+-#include <netpacket/packet.h>
+-#else
+-#include <sys/time.h>
+-#include <sys/ioctl.h>
+-#include <net/bpf.h>
+-struct bpf_insn llcfilter[] = {
+-  BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN),	/* check first byte */
+-  BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5),
+-  BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1),
+-  BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 3),	/* check second byte */
+-  BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2),
+-  BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1),	/* check third byte */
+-  BPF_STMT (BPF_RET + BPF_K, (u_int) - 1),
+-  BPF_STMT (BPF_RET + BPF_K, 0)
+-};
+-int readblen = 0;
+-u_char *readbuff = NULL;
+-#endif /* GNU_LINUX */
+-
+-/*
+- * Table 9 - Architectural constans for use with ISO 8802 subnetworks
+- * ISO 10589 - 8.4.8
+- */
+-
+-u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
+-u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
+-u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
+-u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
+-
+-#ifdef GNU_LINUX
+-static char discard_buff[8192];
+-#endif
+-static char sock_buff[8192];
+-
+-/*
+- * if level is 0 we are joining p2p multicast
+- * FIXME: and the p2p multicast being ???
+- */
+-#ifdef GNU_LINUX
+-static int
+-isis_multicast_join (int fd, int registerto, int if_num)
+-{
+-  struct packet_mreq mreq;
+-
+-  memset (&mreq, 0, sizeof (mreq));
+-  mreq.mr_ifindex = if_num;
+-  if (registerto)
+-    {
+-      mreq.mr_type = PACKET_MR_MULTICAST;
+-      mreq.mr_alen = ETH_ALEN;
+-      if (registerto == 1)
+-	memcpy (&mreq.mr_address, ALL_L1_ISS, ETH_ALEN);
+-      else if (registerto == 2)
+-	memcpy (&mreq.mr_address, ALL_L2_ISS, ETH_ALEN);
+-      else if (registerto == 3)
+-	memcpy (&mreq.mr_address, ALL_ISS, ETH_ALEN);
+-      else
+-	memcpy (&mreq.mr_address, ALL_ESS, ETH_ALEN);
+-
+-    }
+-  else
+-    {
+-      mreq.mr_type = PACKET_MR_ALLMULTI;
+-    }
+-#ifdef EXTREME_DEBUG
+-  zlog_debug ("isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
+-	      "address = %02x:%02x:%02x:%02x:%02x:%02x",
+-	      fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1],
+-	      mreq.mr_address[2], mreq.mr_address[3], mreq.mr_address[4],
+-	      mreq.mr_address[5]);
+-#endif /* EXTREME_DEBUG */
+-  if (setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
+-		  sizeof (struct packet_mreq)))
+-    {
+-      zlog_warn ("isis_multicast_join(): setsockopt(): %s", safe_strerror (errno));
+-      return ISIS_WARNING;
+-    }
+-
+-  return ISIS_OK;
+-}
+-
+-static int
+-open_packet_socket (struct isis_circuit *circuit)
+-{
+-  struct sockaddr_ll s_addr;
+-  int fd, retval = ISIS_OK;
+-
+-  fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
+-  if (fd < 0)
+-    {
+-      zlog_warn ("open_packet_socket(): socket() failed %s",
+-		 safe_strerror (errno));
+-      return ISIS_WARNING;
+-    }
+-
+-  /*
+-   * Bind to the physical interface
+-   */
+-  memset (&s_addr, 0, sizeof (struct sockaddr_ll));
+-  s_addr.sll_family = AF_PACKET;
+-  s_addr.sll_protocol = htons (ETH_P_ALL);
+-  s_addr.sll_ifindex = circuit->interface->ifindex;
+-
+-  if (bind (fd, (struct sockaddr *) (&s_addr),
+-	    sizeof (struct sockaddr_ll)) < 0)
+-    {
+-      zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno));
+-      return ISIS_WARNING;
+-    }
+-
+-  circuit->fd = fd;
+-
+-  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+-    {
+-      /*
+-       * Join to multicast groups
+-       * according to
+-       * 8.4.2 - Broadcast subnetwork IIH PDUs
+-       * FIXME: is there a case only one will fail??
+-       */
+-      if (circuit->circuit_is_type & IS_LEVEL_1)
+-	{
+-	  /* joining ALL_L1_ISS */
+-	  retval = isis_multicast_join (circuit->fd, 1,
+-					circuit->interface->ifindex);
+-	  /* joining ALL_ISS */
+-	  retval = isis_multicast_join (circuit->fd, 3,
+-					circuit->interface->ifindex);
+-	}
+-      if (circuit->circuit_is_type & IS_LEVEL_2)
+-	/* joining ALL_L2_ISS */
+-	retval = isis_multicast_join (circuit->fd, 2,
+-				      circuit->interface->ifindex);
+-    }
+-  else
+-    {
+-      retval =
+-	isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex);
+-    }
+-
+-  return retval;
+-}
+-
+-#else
+-
+-static int
+-open_bpf_dev (struct isis_circuit *circuit)
+-{
+-  int i = 0, fd;
+-  char bpfdev[128];
+-  struct ifreq ifr;
+-  u_int16_t blen;
+-  int true = 1, false = 0;
+-  struct timeval timeout;
+-  struct bpf_program bpf_prog;
+-
+-  do
+-    {
+-      (void) snprintf (bpfdev, sizeof (bpfdev), "/dev/bpf%d", i++);
+-      fd = open (bpfdev, O_RDWR);
+-    }
+-  while (fd < 0 && errno == EBUSY);
+-
+-  if (fd < 0)
+-    {
+-      zlog_warn ("open_bpf_dev(): failed to create bpf socket: %s",
+-		 safe_strerror (errno));
+-      return ISIS_WARNING;
+-    }
+-
+-  zlog_debug ("Opened BPF device %s", bpfdev);
+-
+-  memcpy (ifr.ifr_name, circuit->interface->name, sizeof (ifr.ifr_name));
+-  if (ioctl (fd, BIOCSETIF, (caddr_t) & ifr) < 0)
+-    {
+-      zlog_warn ("open_bpf_dev(): failed to bind to interface: %s",
+-		 safe_strerror (errno));
+-      return ISIS_WARNING;
+-    }
+-
+-  if (ioctl (fd, BIOCGBLEN, (caddr_t) & blen) < 0)
+-    {
+-      zlog_warn ("failed to get BPF buffer len");
+-      blen = circuit->interface->mtu;
+-    }
+-
+-  readblen = blen;
+-
+-  if (readbuff == NULL)
+-    readbuff = malloc (blen);
+-
+-  zlog_debug ("BPF buffer len = %u", blen);
+-
+-  /*  BPF(4): reads return immediately upon packet reception.
+-   *  Otherwise, a read will block until either the kernel
+-   *  buffer becomes full or a timeout occurs. 
+-   */
+-  if (ioctl (fd, BIOCIMMEDIATE, (caddr_t) & true) < 0)
+-    {
+-      zlog_warn ("failed to set BPF dev to immediate mode");
+-    }
+-
+-#ifdef BIOCSSEESENT
+-  /*
+-   * We want to see only incoming packets
+-   */
+-  if (ioctl (fd, BIOCSSEESENT, (caddr_t) & false) < 0)
+-    {
+-      zlog_warn ("failed to set BPF dev to incoming only mode");
+-    }
+-#endif
+-
+-  /*
+-   * ...but all of them
+-   */
+-  if (ioctl (fd, BIOCPROMISC, (caddr_t) & true) < 0)
+-    {
+-      zlog_warn ("failed to set BPF dev to promiscuous mode");
+-    }
+-
+-  /*
+-   * If the buffer length is smaller than our mtu, lets try to increase it
+-   */
+-  if (blen < circuit->interface->mtu)
+-    {
+-      if (ioctl (fd, BIOCSBLEN, &circuit->interface->mtu) < 0)
+-	{
+-	  zlog_warn ("failed to set BPF buffer len (%u to %u)", blen,
+-		     circuit->interface->mtu);
+-	}
+-    }
+-
+-  /*
+-   * Set a timeout parameter - hope this helps select()
+-   */
+-  timeout.tv_sec = 600;
+-  timeout.tv_usec = 0;
+-  if (ioctl (fd, BIOCSRTIMEOUT, (caddr_t) & timeout) < 0)
+-    {
+-      zlog_warn ("failed to set BPF device timeout");
+-    }
+-
+-  /*
+-   * And set the filter
+-   */
+-  memset (&bpf_prog, 0, sizeof (struct bpf_program));
+-  bpf_prog.bf_len = 8;
+-  bpf_prog.bf_insns = &(llcfilter[0]);
+-  if (ioctl (fd, BIOCSETF, (caddr_t) & bpf_prog) < 0)
+-    {
+-      zlog_warn ("open_bpf_dev(): failed to install filter: %s",
+-		 safe_strerror (errno));
+-      return ISIS_WARNING;
+-    }
+-
+-  assert (fd > 0);
+-
+-  circuit->fd = fd;
+-
+-  return ISIS_OK;
+-}
+-
+-#endif /* GNU_LINUX */
+-
+-/*
+- * Create the socket and set the tx/rx funcs
+- */
+-int
+-isis_sock_init (struct isis_circuit *circuit)
+-{
+-  int retval = ISIS_OK;
+-
+-  if (isisd_privs.change (ZPRIVS_RAISE))
+-    zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
+-
+-#ifdef GNU_LINUX
+-  retval = open_packet_socket (circuit);
+-#else
+-  retval = open_bpf_dev (circuit);
+-#endif
+-
+-  if (retval != ISIS_OK)
+-    {
+-      zlog_warn ("%s: could not initialize the socket", __func__);
+-      goto end;
+-    }
+-
+-  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+-    {
+-      circuit->tx = isis_send_pdu_bcast;
+-      circuit->rx = isis_recv_pdu_bcast;
+-    }
+-  else if (circuit->circ_type == CIRCUIT_T_P2P)
+-    {
+-      circuit->tx = isis_send_pdu_p2p;
+-      circuit->rx = isis_recv_pdu_p2p;
+-    }
+-  else
+-    {
+-      zlog_warn ("isis_sock_init(): unknown circuit type");
+-      retval = ISIS_WARNING;
+-      goto end;
+-    }
+-
+-end:
+-  if (isisd_privs.change (ZPRIVS_LOWER))
+-    zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
+-
+-  return retval;
+-}
+-
+-static inline int
+-llc_check (u_char * llc)
+-{
+-  if (*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc + 2) != 3)
+-    return 0;
+-
+-  return 1;
+-}
+-
+-#ifdef GNU_LINUX
+-int
+-isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
+-{
+-  int bytesread, addr_len;
+-  struct sockaddr_ll s_addr;
+-  u_char llc[LLC_LEN];
+-
+-  addr_len = sizeof (s_addr);
+-
+-  memset (&s_addr, 0, sizeof (struct sockaddr_ll));
+-
+-  bytesread = recvfrom (circuit->fd, (void *) &llc,
+-			LLC_LEN, MSG_PEEK,
+-			(struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
+-
+-  if (bytesread < 0)
+-    {
+-      zlog_warn ("isis_recv_packet_bcast(): fd %d, recvfrom (): %s",
+-		 circuit->fd, safe_strerror (errno));
+-      zlog_warn ("circuit is %s", circuit->interface->name);
+-      zlog_warn ("circuit fd %d", circuit->fd);
+-      zlog_warn ("bytesread %d", bytesread);
+-      /* get rid of the packet */
+-      bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
+-      return ISIS_WARNING;
+-    }
+-  /*
+-   * Filtering by llc field, discard packets sent by this host (other circuit)
+-   */
+-  if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING)
+-    {
+-      /*  Read the packet into discard buff */
+-      bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
+-      if (bytesread < 0)
+-	zlog_warn ("isis_recv_pdu_bcast(): read() failed");
+-      return ISIS_WARNING;
+-    }
+-
+-  /* on lan we have to read to the static buff first */
+-  bytesread = recvfrom (circuit->fd, sock_buff, circuit->interface->mtu, 0,
+-			(struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
+-
+-  /* then we lose the LLC */
+-  stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN);
+-
+-  memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
+-
+-  return ISIS_OK;
+-}
+-
+-int
+-isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
+-{
+-  int bytesread, addr_len;
+-  struct sockaddr_ll s_addr;
+-
+-  memset (&s_addr, 0, sizeof (struct sockaddr_ll));
+-  addr_len = sizeof (s_addr);
+-
+-  /* we can read directly to the stream */
+-  bytesread = stream_recvfrom (circuit->rcv_stream, circuit->fd,
+-                               circuit->interface->mtu, 0,
+-                               (struct sockaddr *) &s_addr, 
+-                               (socklen_t *) &addr_len);
+-
+-  if (s_addr.sll_pkttype == PACKET_OUTGOING)
+-    {
+-      /*  Read the packet into discard buff */
+-      bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
+-      if (bytesread < 0)
+-	zlog_warn ("isis_recv_pdu_p2p(): read() failed");
+-      return ISIS_WARNING;
+-    }
+-
+-  /* If we don't have protocol type 0x00FE which is
+-   * ISO over GRE we exit with pain :)
+-   */
+-  if (ntohs (s_addr.sll_protocol) != 0x00FE)
+-    {
+-      zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X",
+-		 ntohs (s_addr.sll_protocol));
+-      return ISIS_WARNING;
+-    }
+-
+-  memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
+-
+-  return ISIS_OK;
+-}
+-
+-int
+-isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
+-{
+-  /* we need to do the LLC in here because of P2P circuits, which will
+-   * not need it
+-   */
+-  int written = 1;
+-  struct sockaddr_ll sa;
+-
+-  stream_set_getp (circuit->snd_stream, 0);
+-  memset (&sa, 0, sizeof (struct sockaddr_ll));
+-  sa.sll_family = AF_PACKET;
+-  sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
+-  sa.sll_ifindex = circuit->interface->ifindex;
+-  sa.sll_halen = ETH_ALEN;
+-  if (level == 1)
+-    memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
+-  else
+-    memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
+-
+-  /* on a broadcast circuit */
+-  /* first we put the LLC in */
+-  sock_buff[0] = 0xFE;
+-  sock_buff[1] = 0xFE;
+-  sock_buff[2] = 0x03;
+-
+-  /* then we copy the data */
+-  memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data,
+-	  stream_get_endp (circuit->snd_stream));
+-
+-  /* now we can send this */
+-  written = sendto (circuit->fd, sock_buff,
+-		    stream_get_endp(circuit->snd_stream) + LLC_LEN, 0,
+-		    (struct sockaddr *) &sa, sizeof (struct sockaddr_ll));
+-
+-  return ISIS_OK;
+-}
+-
+-int
+-isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
+-{
+-
+-  int written = 1;
+-  struct sockaddr_ll sa;
+-
+-  stream_set_getp (circuit->snd_stream, 0);
+-  memset (&sa, 0, sizeof (struct sockaddr_ll));
+-  sa.sll_family = AF_PACKET;
+-  sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
+-  sa.sll_ifindex = circuit->interface->ifindex;
+-  sa.sll_halen = ETH_ALEN;
+-  if (level == 1)
+-    memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
+-  else
+-    memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
+-
+-
+-  /* lets try correcting the protocol */
+-  sa.sll_protocol = htons (0x00FE);
+-  written = sendto (circuit->fd, circuit->snd_stream->data,
+-		    stream_get_endp (circuit->snd_stream), 0, 
+-		    (struct sockaddr *) &sa,
+-		    sizeof (struct sockaddr_ll));
+-
+-  return ISIS_OK;
+-}
+-
+-#else
+-
+-int
+-isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
+-{
+-  int bytesread = 0, bytestoread, offset, one = 1;
+-  struct bpf_hdr *bpf_hdr;
+-
+-  assert (circuit->fd > 0);
+-
+-  if (ioctl (circuit->fd, FIONREAD, (caddr_t) & bytestoread) < 0)
+-    {
+-      zlog_warn ("ioctl() FIONREAD failed: %s", safe_strerror (errno));
+-    }
+-
+-  if (bytestoread)
+-    {
+-      bytesread = read (circuit->fd, readbuff, readblen);
+-    }
+-  if (bytesread < 0)
+-    {
+-      zlog_warn ("isis_recv_pdu_bcast(): read() failed: %s",
+-		 safe_strerror (errno));
+-      return ISIS_WARNING;
+-    }
+-
+-  if (bytesread == 0)
+-    return ISIS_WARNING;
+-
+-  bpf_hdr = (struct bpf_hdr *) readbuff;
+-
+-  assert (bpf_hdr->bh_caplen == bpf_hdr->bh_datalen);
+-
+-  offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN;
+-
+-  /* then we lose the BPF, LLC and ethernet headers */
+-  stream_write (circuit->rcv_stream, readbuff + offset, 
+-                bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN);
+-  stream_set_getp (circuit->rcv_stream, 0);
+-
+-  memcpy (ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN,
+-	  ETHER_ADDR_LEN);
+-
+-  if (ioctl (circuit->fd, BIOCFLUSH, &one) < 0)
+-    zlog_warn ("Flushing failed: %s", safe_strerror (errno));
+-
+-  return ISIS_OK;
+-}
+-
+-int
+-isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
+-{
+-  int bytesread;
+-
+-  bytesread = stream_read (circuit->rcv_stream, circuit->fd, 
+-                           circuit->interface->mtu);
+-
+-  if (bytesread < 0)
+-    {
+-      zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", safe_strerror (errno));
+-      return ISIS_WARNING;
+-    }
+-
+-  return ISIS_OK;
+-}
+-
+-int
+-isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
+-{
+-  struct ether_header *eth;
+-  int written;
+-
+-  stream_set_getp (circuit->snd_stream, 0);
+-
+-  /*
+-   * First the eth header
+-   */
+-  eth = (struct ether_header *) sock_buff;
+-  if (level == 1)
+-    memcpy (eth->ether_dhost, ALL_L1_ISS, ETHER_ADDR_LEN);
+-  else
+-    memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN);
+-  memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN);
+-  eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
+-
+-  /*
+-   * Then the LLC
+-   */
+-  sock_buff[ETHER_HDR_LEN] = ISO_SAP;
+-  sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP;
+-  sock_buff[ETHER_HDR_LEN + 2] = 0x03;
+-
+-  /* then we copy the data */
+-  memcpy (sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data,
+-	  stream_get_endp (circuit->snd_stream));
+-
+-  /* now we can send this */
+-  written = write (circuit->fd, sock_buff,
+-		   stream_get_endp (circuit->snd_stream) 
+-		    + LLC_LEN + ETHER_HDR_LEN);
+-
+-  return ISIS_OK;
+-}
+-
+-int
+-isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
+-{
+-  return ISIS_OK;
+-}
+-
+-#endif /* GNU_LINUX */
+diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h
+index 29c7621..95c1ee4 100644
+--- isisd/isis_pdu.h
++++ isisd/isis_pdu.h
+@@ -24,6 +24,10 @@
+ #ifndef _ZEBRA_ISIS_PDU_H
+ #define _ZEBRA_ISIS_PDU_H
+ 
++#ifdef __SUNPRO_C
++#pragma pack(1)
++#endif
++
+ /*
+  *                    ISO 9542 - 7.5,7.6
+  *
+@@ -222,6 +226,10 @@ struct isis_partial_seqnum_hdr
+ };
+ #define ISIS_PSNP_HDRLEN 9
+ 
++#ifdef __SUNPRO_C
++#pragma pack()
++#endif
++
+ /*
+  * Function for receiving IS-IS PDUs
+  */
+diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c
+new file mode 100644
+index 0000000..8752dba
+--- /dev/null
++++ isisd/isis_pfpacket.c
+@@ -0,0 +1,373 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_pfpacket.c
++ *
++ * Copyright (C) 2001,2002    Sampo Saaristo
++ *                            Tampere University of Technology      
++ *                            Institute of Communications Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#include <zebra.h>
++#include <net/ethernet.h>	/* the L2 protocols */
++#include <netpacket/packet.h>
++
++#include "log.h"
++#include "stream.h"
++#include "if.h"
++
++#include "isisd/dict.h"
++#include "isisd/include-netbsd/iso.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_common.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_flags.h"
++#include "isisd/isisd.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_network.h"
++
++#include "privs.h"
++
++extern struct zebra_privs_t isisd_privs;
++
++/*
++ * Table 9 - Architectural constants for use with ISO 8802 subnetworks
++ * ISO 10589 - 8.4.8
++ */
++
++u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
++u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
++u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
++u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
++
++static char discard_buff[8192];
++static char sock_buff[8192];
++
++/*
++ * if level is 0 we are joining p2p multicast
++ * FIXME: and the p2p multicast being ???
++ */
++static int
++isis_multicast_join (int fd, int registerto, int if_num)
++{
++  struct packet_mreq mreq;
++
++  memset (&mreq, 0, sizeof (mreq));
++  mreq.mr_ifindex = if_num;
++  if (registerto)
++    {
++      mreq.mr_type = PACKET_MR_MULTICAST;
++      mreq.mr_alen = ETH_ALEN;
++      if (registerto == 1)
++	memcpy (&mreq.mr_address, ALL_L1_ISS, ETH_ALEN);
++      else if (registerto == 2)
++	memcpy (&mreq.mr_address, ALL_L2_ISS, ETH_ALEN);
++      else if (registerto == 3)
++	memcpy (&mreq.mr_address, ALL_ISS, ETH_ALEN);
++      else
++	memcpy (&mreq.mr_address, ALL_ESS, ETH_ALEN);
++
++    }
++  else
++    {
++      mreq.mr_type = PACKET_MR_ALLMULTI;
++    }
++#ifdef EXTREME_DEBUG
++  zlog_debug ("isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
++	      "address = %02x:%02x:%02x:%02x:%02x:%02x",
++	      fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1],
++	      mreq.mr_address[2], mreq.mr_address[3], mreq.mr_address[4],
++	      mreq.mr_address[5]);
++#endif /* EXTREME_DEBUG */
++  if (setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
++		  sizeof (struct packet_mreq)))
++    {
++      zlog_warn ("isis_multicast_join(): setsockopt(): %s", safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  return ISIS_OK;
++}
++
++static int
++open_packet_socket (struct isis_circuit *circuit)
++{
++  struct sockaddr_ll s_addr;
++  int fd, retval = ISIS_OK;
++
++  fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
++  if (fd < 0)
++    {
++      zlog_warn ("open_packet_socket(): socket() failed %s",
++		 safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  /*
++   * Bind to the physical interface
++   */
++  memset (&s_addr, 0, sizeof (struct sockaddr_ll));
++  s_addr.sll_family = AF_PACKET;
++  s_addr.sll_protocol = htons (ETH_P_ALL);
++  s_addr.sll_ifindex = circuit->interface->ifindex;
++
++  if (bind (fd, (struct sockaddr *) (&s_addr),
++	    sizeof (struct sockaddr_ll)) < 0)
++    {
++      zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno));
++      return ISIS_WARNING;
++    }
++
++  circuit->fd = fd;
++
++  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
++    {
++      /*
++       * Join to multicast groups
++       * according to
++       * 8.4.2 - Broadcast subnetwork IIH PDUs
++       * FIXME: is there a case only one will fail??
++       */
++      if (circuit->circuit_is_type & IS_LEVEL_1)
++	{
++	  /* joining ALL_L1_ISS */
++	  retval = isis_multicast_join (circuit->fd, 1,
++					circuit->interface->ifindex);
++	  /* joining ALL_ISS */
++	  retval = isis_multicast_join (circuit->fd, 3,
++					circuit->interface->ifindex);
++	}
++      if (circuit->circuit_is_type & IS_LEVEL_2)
++	/* joining ALL_L2_ISS */
++	retval = isis_multicast_join (circuit->fd, 2,
++				      circuit->interface->ifindex);
++    }
++  else
++    {
++      retval =
++	isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex);
++    }
++
++  return retval;
++}
++
++/*
++ * Create the socket and set the tx/rx funcs
++ */
++int
++isis_sock_init (struct isis_circuit *circuit)
++{
++  int retval = ISIS_OK;
++
++  if (isisd_privs.change (ZPRIVS_RAISE))
++    zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
++
++  retval = open_packet_socket (circuit);
++
++  if (retval != ISIS_OK)
++    {
++      zlog_warn ("%s: could not initialize the socket", __func__);
++      goto end;
++    }
++
++  if (circuit->circ_type == CIRCUIT_T_BROADCAST)
++    {
++      circuit->tx = isis_send_pdu_bcast;
++      circuit->rx = isis_recv_pdu_bcast;
++    }
++  else if (circuit->circ_type == CIRCUIT_T_P2P)
++    {
++      circuit->tx = isis_send_pdu_p2p;
++      circuit->rx = isis_recv_pdu_p2p;
++    }
++  else
++    {
++      zlog_warn ("isis_sock_init(): unknown circuit type");
++      retval = ISIS_WARNING;
++      goto end;
++    }
++
++end:
++  if (isisd_privs.change (ZPRIVS_LOWER))
++    zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
++
++  return retval;
++}
++
++static inline int
++llc_check (u_char * llc)
++{
++  if (*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc + 2) != 3)
++    return 0;
++
++  return 1;
++}
++
++int
++isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
++{
++  int bytesread, addr_len;
++  struct sockaddr_ll s_addr;
++  u_char llc[LLC_LEN];
++
++  addr_len = sizeof (s_addr);
++
++  memset (&s_addr, 0, sizeof (struct sockaddr_ll));
++
++  bytesread = recvfrom (circuit->fd, (void *) &llc,
++			LLC_LEN, MSG_PEEK,
++			(struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
++
++  if (bytesread < 0)
++    {
++      zlog_warn ("isis_recv_packet_bcast(): fd %d, recvfrom (): %s",
++		 circuit->fd, safe_strerror (errno));
++      zlog_warn ("circuit is %s", circuit->interface->name);
++      zlog_warn ("circuit fd %d", circuit->fd);
++      zlog_warn ("bytesread %d", bytesread);
++      /* get rid of the packet */
++      bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
++      return ISIS_WARNING;
++    }
++  /*
++   * Filtering by llc field, discard packets sent by this host (other circuit)
++   */
++  if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING)
++    {
++      /*  Read the packet into discard buff */
++      bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
++      if (bytesread < 0)
++	zlog_warn ("isis_recv_pdu_bcast(): read() failed");
++      return ISIS_WARNING;
++    }
++
++  /* on lan we have to read to the static buff first */
++  bytesread = recvfrom (circuit->fd, sock_buff, circuit->interface->mtu, 0,
++			(struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
++
++  /* then we lose the LLC */
++  stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN);
++
++  memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
++
++  return ISIS_OK;
++}
++
++int
++isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
++{
++  int bytesread, addr_len;
++  struct sockaddr_ll s_addr;
++
++  memset (&s_addr, 0, sizeof (struct sockaddr_ll));
++  addr_len = sizeof (s_addr);
++
++  /* we can read directly to the stream */
++  bytesread = stream_recvfrom (circuit->rcv_stream, circuit->fd,
++                               circuit->interface->mtu, 0,
++                               (struct sockaddr *) &s_addr, 
++                               (socklen_t *) &addr_len);
++
++  if (s_addr.sll_pkttype == PACKET_OUTGOING)
++    {
++      /*  Read the packet into discard buff */
++      bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
++      if (bytesread < 0)
++	zlog_warn ("isis_recv_pdu_p2p(): read() failed");
++      return ISIS_WARNING;
++    }
++
++  /* If we don't have protocol type 0x00FE which is
++   * ISO over GRE we exit with pain :)
++   */
++  if (ntohs (s_addr.sll_protocol) != 0x00FE)
++    {
++      zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X",
++		 ntohs (s_addr.sll_protocol));
++      return ISIS_WARNING;
++    }
++
++  memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
++
++  return ISIS_OK;
++}
++
++int
++isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
++{
++  /* we need to do the LLC in here because of P2P circuits, which will
++   * not need it
++   */
++  int written = 1;
++  struct sockaddr_ll sa;
++
++  stream_set_getp (circuit->snd_stream, 0);
++  memset (&sa, 0, sizeof (struct sockaddr_ll));
++  sa.sll_family = AF_PACKET;
++  sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
++  sa.sll_ifindex = circuit->interface->ifindex;
++  sa.sll_halen = ETH_ALEN;
++  if (level == 1)
++    memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
++  else
++    memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
++
++  /* on a broadcast circuit */
++  /* first we put the LLC in */
++  sock_buff[0] = 0xFE;
++  sock_buff[1] = 0xFE;
++  sock_buff[2] = 0x03;
++
++  /* then we copy the data */
++  memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data,
++	  stream_get_endp (circuit->snd_stream));
++
++  /* now we can send this */
++  written = sendto (circuit->fd, sock_buff,
++		    stream_get_endp(circuit->snd_stream) + LLC_LEN, 0,
++		    (struct sockaddr *) &sa, sizeof (struct sockaddr_ll));
++
++  return ISIS_OK;
++}
++
++int
++isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
++{
++
++  int written = 1;
++  struct sockaddr_ll sa;
++
++  stream_set_getp (circuit->snd_stream, 0);
++  memset (&sa, 0, sizeof (struct sockaddr_ll));
++  sa.sll_family = AF_PACKET;
++  sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
++  sa.sll_ifindex = circuit->interface->ifindex;
++  sa.sll_halen = ETH_ALEN;
++  if (level == 1)
++    memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
++  else
++    memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
++
++
++  /* lets try correcting the protocol */
++  sa.sll_protocol = htons (0x00FE);
++  written = sendto (circuit->fd, circuit->snd_stream->data,
++		    stream_get_endp (circuit->snd_stream), 0, 
++		    (struct sockaddr *) &sa,
++		    sizeof (struct sockaddr_ll));
++
++  return ISIS_OK;
++}
+diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h
+index 951a254..fc9f35f 100644
+--- isisd/isis_tlv.h
++++ isisd/isis_tlv.h
+@@ -152,6 +152,10 @@ struct lan_neigh
+   u_char LAN_addr[6];
+ };
+ 
++#ifdef __SUNPRO_C
++#pragma pack(1)
++#endif
++
+ /* struct for LSP entry */
+ struct lsp_entry
+ {
+@@ -161,6 +165,10 @@ struct lsp_entry
+   u_int16_t checksum;
+ } __attribute__ ((packed));
+ 
++#ifdef __SUNPRO_C
++#pragma pack()
++#endif
++
+ /* struct for checksum */
+ struct checksum
+ {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/30-ospfd-nssa-asbr.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,27 @@
+diff --git a/ospfd/ChangeLog b/ospfd/ChangeLog
+index 24c3d29..bb0e908 100644
+--- ospfd/ChangeLog
++++ ospfd/ChangeLog
+@@ -1,3 +1,8 @@
++2007-08-06 Paul Jakma <[email protected]>
++
++	* ospf_lsa.c: (router_lsa_flags) Bug #331, NSSA regression caused
++	  caused ASBRs to not advertise E-bit into NSSA areas.
++
+ 2007-05-09 Milan Kocian <[email protected]>
+ 
+ 	* ospf_vty.c: Fix commands: 'ip ospf authentication A.B.C.D',
+diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
+index b9a70a6..243928f 100644
+--- ospfd/ospf_lsa.c
++++ ospfd/ospf_lsa.c
+@@ -437,8 +437,7 @@ router_lsa_flags (struct ospf_area *area)
+ 	SET_FLAG (flags, ROUTER_LSA_SHORTCUT);
+ 
+   /* ASBR can't exit in stub area. */
+-  if (area->external_routing == OSPF_AREA_STUB
+-      || area->external_routing == OSPF_AREA_NSSA)
++  if (area->external_routing == OSPF_AREA_STUB)
+     UNSET_FLAG (flags, ROUTER_LSA_EXTERNAL);
+   /* If ASBR set External flag */
+   else if (IS_OSPF_ASBR (area->ospf))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/35-ospfd-spf-sort.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,71 @@
+commit 7591d8b862439dfae8b4b16d148ce567b6ff8cb7
+Author: Paul Jakma <[email protected]>
+Date:   Mon Aug 6 18:52:45 2007 +0000
+
+    [ospfd] Fix bad SPF calculation on some topologies - incorrect sorting
+    
+    2007-08-07 Atis Elsts <[email protected]>
+    
+    	* ospf_spf.c: (ospf_spf_next) Sort heap in correct direction
+    	  after vertex cost is changed, thus fixing incorrect SPF
+    	  calculation on certain topologies.
+    	* lib/pqueue.{c,h}: Export trickle_up
+
+diff --git a/lib/pqueue.c b/lib/pqueue.c
+index a974a49..12a779f 100644
+--- lib/pqueue.c
++++ lib/pqueue.c
+@@ -42,7 +42,7 @@ Boston, MA 02111-1307, USA.  */
+ #define RIGHT_OF(x) (2 * x + 2)
+ #define HAVE_CHILD(x,q) (x < (q)->size / 2)
+ 
+-static void
++void
+ trickle_up (int index, struct pqueue *queue)
+ {
+   void *tmp;
+diff --git a/lib/pqueue.h b/lib/pqueue.h
+index 1f3201b..be37f98 100644
+--- lib/pqueue.h
++++ lib/pqueue.h
+@@ -40,5 +40,6 @@ extern void pqueue_enqueue (void *data, struct pqueue *queue);
+ extern void *pqueue_dequeue (struct pqueue *queue);
+ 
+ extern void trickle_down (int index, struct pqueue *queue);
++extern void trickle_up (int index, struct pqueue *queue);
+ 
+ #endif /* _ZEBRA_PQUEUE_H */
+diff --git a/ospfd/ChangeLog b/ospfd/ChangeLog
+index bb0e908..422208e 100644
+--- ospfd/ChangeLog
++++ ospfd/ChangeLog
+@@ -1,3 +1,9 @@
++2007-08-07 Atis Elsts <[email protected]>
++
++	* ospf_spf.c: (ospf_spf_next) Sort heap in correct direction
++	  after vertex cost is changed, thus fixing incorrect SPF
++	  calculation on certain topologies.
++
+ 2007-08-06 Paul Jakma <[email protected]>
+ 
+ 	* ospf_lsa.c: (router_lsa_flags) Bug #331, NSSA regression caused
+diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
+index 51e3383..41288f1 100644
+--- ospfd/ospf_spf.c
++++ ospfd/ospf_spf.c
+@@ -896,9 +896,12 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area,
+                * will flush the old parents
+                */
+               if (ospf_nexthop_calculation (area, v, w, l, distance))
+-                /* Decrease the key of the node in the heap,
+-                 * re-sort the heap. */
+-                trickle_down (w_lsa->stat, candidate);
++                /* Decrease the key of the node in the heap.
++                 * trickle-sort it up towards root, just in case this
++                 * node should now be the new root due the cost change. 
++                 * (pqueu_{de,en}queue 
++                 */
++                trickle_up (w_lsa->stat, candidate);
+             }
+         } /* end W is already on the candidate list */
+     } /* end loop over the links in V's LSA */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/40-bgp-capab-cleanup.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,1298 @@
+commit 6d58272b4cf96f0daa846210dd2104877900f921
+Author: Paul Jakma <[email protected]>
+Date:   Mon Aug 6 15:21:45 2007 +0000
+
+    [bgpd] cleanup, compact and consolidate capability parsing code
+    
+    2007-07-26 Paul Jakma <[email protected]>
+    
+    	* (general) Clean up and compact capability parsing slightly.
+    	  Consolidate validation of length and logging of generic TLV, and
+    	  memcpy of capability data, thus removing such from cap specifc
+    	  code (not always present or correct).
+    	* bgp_open.h: Add structures for the generic capability TLV header
+    	  and for the data formats of the various specific capabilities we
+    	  support.  Hence remove the badly named, or else misdefined, struct
+    	  capability.
+    	* bgp_open.c: (bgp_capability_vty_out) Use struct capability_mp_data.
+    	  Do the length checks *before* memcpy()'ing based on that length
+    	  (stored capability - should have been validated anyway on input,
+    	  but..).
+    	  (bgp_afi_safi_valid_indices) new function to validate (afi,safi)
+    	  which is about to be used as index into arrays, consolidates
+    	  several instances of same, at least one of which appeared to be
+    	  incomplete..
+    	  (bgp_capability_mp) Much condensed.
+    	  (bgp_capability_orf_entry) New, process one ORF entry
+    	  (bgp_capability_orf) Condensed. Fixed to process all ORF entries.
+    	  (bgp_capability_restart) Condensed, and fixed to use a
+    	  cap-specific type, rather than abusing capability_mp.
+    	  (struct message capcode_str) added to aid generic logging.
+    	  (size_t cap_minsizes[]) added to aid generic validation of
+    	  capability length field.
+    	  (bgp_capability_parse) Generic logging and validation of TLV
+    	  consolidated here. Code compacted as much as possible.
+    	* bgp_packet.c: (bgp_open_receive) Capability parsers now use
+    	  streams, so no more need here to manually fudge the input stream
+    	  getp.
+    	  (bgp_capability_msg_parse) use struct capability_mp_data. Validate
+    	  lengths /before/ memcpy. Use bgp_afi_safi_valid_indices.
+    	  (bgp_capability_receive) Exported for use by test harness.
+    	* bgp_vty.c: (bgp_show_summary) fix conversion warning
+    	  (bgp_show_peer) ditto
+    	* bgp_debug.h: Fix storage 'extern' after type 'const'.
+            * lib/log.c: (mes_lookup) warning about code not being in
+              same-number array slot should be debug, not warning. E.g. BGP
+              has several discontigious number spaces, allocating from
+              different parts of a space is not uncommon (e.g. IANA
+              assigned versus vendor-assigned code points in some number
+              space).
+
+--- bgpd/bgp_debug.h
++++ bgpd/bgp_debug.h
+@@ -110,7 +110,7 @@ extern unsigned long term_bgp_debug_zebra;
+ #define BGP_DEBUG(a, b)		(term_bgp_debug_ ## a & BGP_DEBUG_ ## b)
+ #define CONF_BGP_DEBUG(a, b)    (conf_bgp_debug_ ## a & BGP_DEBUG_ ## b)
+ 
+-const extern char *bgp_type_str[];
++extern const char *bgp_type_str[];
+ 
+ extern int bgp_dump_attr (struct peer *, struct attr *, char *, size_t);
+ extern void bgp_notify_print (struct peer *, struct bgp_notify *, const char *);
+diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
+index e44bd2a..d4f7cdf 100644
+--- bgpd/bgp_open.c
++++ bgpd/bgp_open.c
+@@ -26,6 +26,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ #include "thread.h"
+ #include "log.h"
+ #include "command.h"
++#include "memory.h"
+ 
+ #include "bgpd/bgpd.h"
+ #include "bgpd/bgp_attr.h"
+@@ -50,25 +51,28 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer)
+ {
+   char *pnt;
+   char *end;
+-  struct capability cap;
++  struct capability_mp_data mpc;
++  struct capability_header *hdr;
+ 
+   pnt = peer->notify.data;
+   end = pnt + peer->notify.length;
+-
++  
+   while (pnt < end)
+     {
+-      memcpy(&cap, pnt, sizeof(struct capability));
+-
+-      if (pnt + 2 > end)
++      if (pnt + sizeof (struct capability_mp_data) + 2 > end)
+ 	return;
+-      if (pnt + (cap.length + 2) > end)
++      
++      hdr = (struct capability_header *)pnt;
++      if (pnt + hdr->length + 2 > end)
+ 	return;
+ 
+-      if (cap.code == CAPABILITY_CODE_MP)
++      memcpy (&mpc, pnt + 2, sizeof(struct capability_mp_data));
++
++      if (hdr->code == CAPABILITY_CODE_MP)
+ 	{
+ 	  vty_out (vty, "  Capability error for: Multi protocol ");
+ 
+-	  switch (ntohs (cap.mpc.afi))
++	  switch (ntohs (mpc.afi))
+ 	    {
+ 	    case AFI_IP:
+ 	      vty_out (vty, "AFI IPv4, ");
+@@ -77,10 +81,10 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer)
+ 	      vty_out (vty, "AFI IPv6, ");
+ 	      break;
+ 	    default:
+-	      vty_out (vty, "AFI Unknown %d, ", ntohs (cap.mpc.afi));
++	      vty_out (vty, "AFI Unknown %d, ", ntohs (mpc.afi));
+ 	      break;
+ 	    }
+-	  switch (cap.mpc.safi)
++	  switch (mpc.safi)
+ 	    {
+ 	    case SAFI_UNICAST:
+ 	      vty_out (vty, "SAFI Unicast");
+@@ -95,88 +99,87 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer)
+ 	      vty_out (vty, "SAFI MPLS-VPN");
+ 	      break;
+ 	    default:
+-	      vty_out (vty, "SAFI Unknown %d ", cap.mpc.safi);
++	      vty_out (vty, "SAFI Unknown %d ", mpc.safi);
+ 	      break;
+ 	    }
+ 	  vty_out (vty, "%s", VTY_NEWLINE);
+ 	}
+-      else if (cap.code >= 128)
++      else if (hdr->code >= 128)
+ 	vty_out (vty, "  Capability error: vendor specific capability code %d",
+-		 cap.code);
++		 hdr->code);
+       else
+ 	vty_out (vty, "  Capability error: unknown capability code %d", 
+-		 cap.code);
++		 hdr->code);
+ 
+-      pnt += cap.length + 2;
++      pnt += hdr->length + 2;
+     }
+ }
+ 
+-/* Set negotiated capability value. */
+-static int
+-bgp_capability_mp (struct peer *peer, struct capability *cap)
++static void 
++bgp_capability_mp_data (struct stream *s, struct capability_mp_data *mpc)
+ {
+-  if (ntohs (cap->mpc.afi) == AFI_IP)
+-    {
+-      if (cap->mpc.safi == SAFI_UNICAST)
+-	{
+-	  peer->afc_recv[AFI_IP][SAFI_UNICAST] = 1;
+-
+-	  if (peer->afc[AFI_IP][SAFI_UNICAST])
+-	    peer->afc_nego[AFI_IP][SAFI_UNICAST] = 1;
+-	  else
+-	    return -1;
+-	}
+-      else if (cap->mpc.safi == SAFI_MULTICAST) 
+-	{
+-	  peer->afc_recv[AFI_IP][SAFI_MULTICAST] = 1;
+-
+-	  if (peer->afc[AFI_IP][SAFI_MULTICAST])
+-	    peer->afc_nego[AFI_IP][SAFI_MULTICAST] = 1;
+-	  else
+-	    return -1;
+-	}
+-      else if (cap->mpc.safi == BGP_SAFI_VPNV4)
+-	{
+-	  peer->afc_recv[AFI_IP][SAFI_MPLS_VPN] = 1;
++  mpc->afi = stream_getw (s);
++  mpc->reserved = stream_getc (s);
++  mpc->safi = stream_getc (s);
++}
+ 
+-	  if (peer->afc[AFI_IP][SAFI_MPLS_VPN])
+-	    peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] = 1;
+-	  else
+-	    return -1;
+-	}
+-      else
+-	return -1;
+-    }
+-#ifdef HAVE_IPV6
+-  else if (ntohs (cap->mpc.afi) == AFI_IP6)
++int
++bgp_afi_safi_valid_indices (afi_t afi, safi_t *safi)
++{
++  /* VPNvX are AFI specific */
++  if ((afi == AFI_IP6 && *safi == BGP_SAFI_VPNV4)
++      || (afi == AFI_IP && *safi == BGP_SAFI_VPNV6))
+     {
+-      if (cap->mpc.safi == SAFI_UNICAST)
+-	{
+-	  peer->afc_recv[AFI_IP6][SAFI_UNICAST] = 1;
+-
+-	  if (peer->afc[AFI_IP6][SAFI_UNICAST])
+-	    peer->afc_nego[AFI_IP6][SAFI_UNICAST] = 1;
+-	  else
+-	    return -1;
+-	}
+-      else if (cap->mpc.safi == SAFI_MULTICAST)
+-	{
+-	  peer->afc_recv[AFI_IP6][SAFI_MULTICAST] = 1;
+-
+-	  if (peer->afc[AFI_IP6][SAFI_MULTICAST])
+-	    peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = 1;
+-	  else
+-	    return -1;
+-	}
+-      else
+-	return -1;
++      zlog_warn ("Invalid afi/safi combination (%u/%u)", afi, *safi);
++      return 0;
+     }
+-#endif /* HAVE_IPV6 */
+-  else
++  
++  switch (afi)
+     {
+-      /* Unknown Address Family. */
+-      return -1;
++      case AFI_IP:
++#ifdef HAVE_IPV6
++      case AFI_IP6:
++#endif
++        switch (*safi)
++          {
++            /* BGP VPNvX SAFI isn't contigious with others, remap */
++            case BGP_SAFI_VPNV4:
++            case BGP_SAFI_VPNV6:
++              *safi = SAFI_MPLS_VPN;
++            case SAFI_UNICAST:
++            case SAFI_MULTICAST:
++            case SAFI_MPLS_VPN:
++              return 1;
++          }
+     }
++  zlog_debug ("unknown afi/safi (%u/%u)", afi, *safi);
++  
++  return 0;
++}
++
++/* Set negotiated capability value. */
++static int
++bgp_capability_mp (struct peer *peer, struct capability_header *hdr)
++{
++  struct capability_mp_data mpc;
++  struct stream *s = BGP_INPUT (peer);
++  
++  bgp_capability_mp_data (s, &mpc);
++  
++  if (BGP_DEBUG (normal, NORMAL))
++    zlog_debug ("%s OPEN has MP_EXT CAP for afi/safi: %u/%u",
++               peer->host, mpc.afi, mpc.safi);
++  
++  if (!bgp_afi_safi_valid_indices (mpc.afi, &mpc.safi))
++    return -1;
++   
++  /* Now safi remapped, and afi/safi are valid array indices */
++  peer->afc_recv[mpc.afi][mpc.safi] = 1;
++  
++  if (peer->afc[mpc.afi][mpc.safi])
++    peer->afc_nego[mpc.safi][mpc.safi] = 1;
++  else 
++    return -1;
+ 
+   return 0;
+ }
+@@ -190,98 +193,133 @@ bgp_capability_orf_not_support (struct peer *peer, afi_t afi, safi_t safi,
+ 	       peer->host, afi, safi, type, mode);
+ }
+ 
++static struct message orf_type_str[] =
++{
++  { ORF_TYPE_PREFIX,		"Prefixlist"		},
++  { ORF_TYPE_PREFIX_OLD,	"Prefixlist (old)"	},
++};
++static int orf_type_str_max = sizeof(orf_type_str)/sizeof(orf_type_str[0]);
++
++static struct message orf_mode_str[] =
++{
++  { ORF_MODE_RECEIVE,	"Receive"	},
++  { ORF_MODE_SEND,	"Send"		},
++  { ORF_MODE_BOTH,	"Both"		},
++};
++static int orf_mode_str_max = sizeof(orf_mode_str)/sizeof(orf_mode_str[0]);
++
+ static int
+-bgp_capability_orf (struct peer *peer, struct capability *cap,
+-		    u_char *pnt)
++bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr)
+ {
+-  afi_t afi = ntohs(cap->mpc.afi);
+-  safi_t safi = cap->mpc.safi;
+-  u_char number_of_orfs;
++  struct stream *s = BGP_INPUT (peer);
++  struct capability_orf_entry entry;
++  afi_t afi;
++  safi_t safi;
+   u_char type;
+   u_char mode;
+   u_int16_t sm_cap = 0; /* capability send-mode receive */
+   u_int16_t rm_cap = 0; /* capability receive-mode receive */ 
+   int i;
+ 
+-  /* Check length. */
+-  if (cap->length < 7)
+-    {
+-      zlog_info ("%s ORF Capability length error %d",
+-		 peer->host, cap->length);
+-		 bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+-      return -1;
+-    }
+-
++  /* ORF Entry header */
++  bgp_capability_mp_data (s, &entry.mpc);
++  entry.num = stream_getc (s);
++  afi = entry.mpc.afi;
++  safi = entry.mpc.safi;
++  
+   if (BGP_DEBUG (normal, NORMAL))
+-    zlog_debug ("%s OPEN has ORF CAP(%s) for afi/safi: %u/%u",
+-	       peer->host, (cap->code == CAPABILITY_CODE_ORF ?
+-                       "new" : "old"), afi, safi);
++    zlog_debug ("%s ORF Cap entry for afi/safi: %u/%u",
++	        peer->host, entry.mpc.afi, entry.mpc.safi);
+ 
+   /* Check AFI and SAFI. */
+-  if ((afi != AFI_IP && afi != AFI_IP6)
+-      || (safi != SAFI_UNICAST && safi != SAFI_MULTICAST
+-	  && safi != BGP_SAFI_VPNV4))
++  if (!bgp_afi_safi_valid_indices (entry.mpc.afi, &safi))
++    {
++      zlog_info ("%s Addr-family %d/%d not supported."
++                 " Ignoring the ORF capability",
++                 peer->host, entry.mpc.afi, entry.mpc.safi);
++      return 0;
++    }
++  
++  /* validate number field */
++  if (sizeof (struct capability_orf_entry) + (entry.num * 2) > hdr->length)
+     {
+-      zlog_info ("%s Addr-family %d/%d not supported. Ignoring the ORF capability",
+-                 peer->host, afi, safi);
++      zlog_info ("%s ORF Capability entry length error,"
++                 " Cap length %u, num %u",
++                 peer->host, hdr->length, entry.num);
++      bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+       return -1;
+     }
+ 
+-  number_of_orfs = *pnt++;
+-
+-  for (i = 0 ; i < number_of_orfs ; i++)
++  for (i = 0 ; i < entry.num ; i++)
+     {
+-      type = *pnt++;
+-      mode = *pnt++;
+-
++      type = stream_getc(s);
++      mode = stream_getc(s);
++      
+       /* ORF Mode error check */
+-      if (mode != ORF_MODE_BOTH && mode != ORF_MODE_SEND
+-	  && mode != ORF_MODE_RECEIVE)
+-	{
+-	  bgp_capability_orf_not_support (peer, afi, safi, type, mode);
+-	  continue;
++      switch (mode)
++        {
++          case ORF_MODE_BOTH:
++          case ORF_MODE_SEND:
++          case ORF_MODE_RECEIVE:
++            break;
++          default:
++	    bgp_capability_orf_not_support (peer, afi, safi, type, mode);
++	    continue;
+ 	}
++      /* ORF Type and afi/safi error checks */
++      /* capcode versus type */
++      switch (hdr->code)
++        {
++          case CAPABILITY_CODE_ORF:
++            switch (type)
++              {
++                case ORF_TYPE_PREFIX:
++                  break;
++                default:
++                  bgp_capability_orf_not_support (peer, afi, safi, type, mode);
++                  continue;
++              }
++            break;
++          case CAPABILITY_CODE_ORF_OLD:
++            switch (type)
++              {
++                case ORF_TYPE_PREFIX_OLD:
++                  break;
++                default:
++                  bgp_capability_orf_not_support (peer, afi, safi, type, mode);
++                  continue;
++              }
++            break;
++          default:
++            bgp_capability_orf_not_support (peer, afi, safi, type, mode);
++            continue;
++        }
++                
++      /* AFI vs SAFI */
++      if (!((afi == AFI_IP && safi == SAFI_UNICAST)
++            || (afi == AFI_IP && safi == SAFI_MULTICAST)
++            || (afi == AFI_IP6 && safi == SAFI_UNICAST)))
++        {
++          bgp_capability_orf_not_support (peer, afi, safi, type, mode);
++          continue;
++        }
++      
++      if (BGP_DEBUG (normal, NORMAL))
++        zlog_debug ("%s OPEN has %s ORF capability"
++                    " as %s for afi/safi: %d/%d",
++                    peer->host, LOOKUP (orf_type_str, type),
++                    LOOKUP (orf_mode_str, mode),
++                    entry.mpc.afi, safi);
+ 
+-      /* ORF Type and afi/safi error check */
+-      if (cap->code == CAPABILITY_CODE_ORF)
++      if (hdr->code == CAPABILITY_CODE_ORF)
+ 	{
+-	  if (type == ORF_TYPE_PREFIX &&
+-	      ((afi == AFI_IP && safi == SAFI_UNICAST)
+-		|| (afi == AFI_IP && safi == SAFI_MULTICAST)
+-		|| (afi == AFI_IP6 && safi == SAFI_UNICAST)))
+-	    {
+-	      sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV;
+-	      rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV;
+-	      if (BGP_DEBUG (normal, NORMAL))
+-		zlog_debug ("%s OPEN has Prefixlist ORF(%d) capability as %s for afi/safi: %d/%d",
+-			   peer->host, ORF_TYPE_PREFIX, (mode == ORF_MODE_SEND ? "SEND" :
+-			   mode == ORF_MODE_RECEIVE ? "RECEIVE" : "BOTH") , afi, safi);
+-	    }
+-	  else
+-	    {
+-	      bgp_capability_orf_not_support (peer, afi, safi, type, mode);
+-	      continue;
+-	    }
++          sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV;
++          rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV;
+ 	}
+-      else if (cap->code == CAPABILITY_CODE_ORF_OLD)
++      else if (hdr->code == CAPABILITY_CODE_ORF_OLD)
+ 	{
+-	  if (type == ORF_TYPE_PREFIX_OLD &&
+-	      ((afi == AFI_IP && safi == SAFI_UNICAST)
+-		|| (afi == AFI_IP && safi == SAFI_MULTICAST)
+-		|| (afi == AFI_IP6 && safi == SAFI_UNICAST)))
+-	    {
+-	      sm_cap = PEER_CAP_ORF_PREFIX_SM_OLD_RCV;
+-	      rm_cap = PEER_CAP_ORF_PREFIX_RM_OLD_RCV;
+-	      if (BGP_DEBUG (normal, NORMAL))
+-		zlog_debug ("%s OPEN has Prefixlist ORF(%d) capability as %s for afi/safi: %d/%d",
+-			   peer->host, ORF_TYPE_PREFIX_OLD, (mode == ORF_MODE_SEND ? "SEND" :
+-			   mode == ORF_MODE_RECEIVE ? "RECEIVE" : "BOTH") , afi, safi);
+-	    }
+-	  else
+-	    {
+-	      bgp_capability_orf_not_support (peer, afi, safi, type, mode);
+-	      continue;
+-	    }
++          sm_cap = PEER_CAP_ORF_PREFIX_SM_OLD_RCV;
++          rm_cap = PEER_CAP_ORF_PREFIX_RM_OLD_RCV;
+ 	}
+       else
+ 	{
+@@ -306,206 +344,258 @@ bgp_capability_orf (struct peer *peer, struct capability *cap,
+   return 0;
+ }
+ 
+-/* Parse given capability. */
+ static int
+-bgp_capability_parse (struct peer *peer, u_char *pnt, u_char length,
+-		      u_char **error)
++bgp_capability_orf (struct peer *peer, struct capability_header *hdr)
+ {
+-  int ret;
+-  u_char *end;
+-  struct capability cap;
+-
+-  end = pnt + length;
+-
+-  while (pnt < end)
++  struct stream *s = BGP_INPUT (peer);
++  size_t end = stream_get_getp (s) + hdr->length;
++  
++  assert (stream_get_getp(s) + sizeof(struct capability_orf_entry) <= end);
++  
++  /* We must have at least one ORF entry, as the caller has already done
++   * minimum length validation for the capability code - for ORF there must
++   * at least one ORF entry (header and unknown number of pairs of bytes).
++   */
++  do
+     {
+-      afi_t afi;
+-      safi_t safi;
++      if (bgp_capability_orf_entry (peer, hdr) == -1)
++        return -1;
++    } 
++  while (stream_get_getp(s) + sizeof(struct capability_orf_entry) < end);
++  
++  return 0;
++}
+ 
+-      /* Fetch structure to the byte stream. */
+-      memcpy (&cap, pnt, sizeof (struct capability));
++static int
++bgp_capability_restart (struct peer *peer, struct capability_header *caphdr)
++{
++  struct stream *s = BGP_INPUT (peer);
++  u_int16_t restart_flag_time;
++  int restart_bit = 0;
++  size_t end = stream_get_getp (s) + caphdr->length;
++
++  SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV);
++  restart_flag_time = stream_getw(s);
++  if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT))
++    restart_bit = 1;
++  UNSET_FLAG (restart_flag_time, 0xF000);
++  peer->v_gr_restart = restart_flag_time;
+ 
+-      afi = ntohs(cap.mpc.afi);
+-      safi = cap.mpc.safi;
++  if (BGP_DEBUG (normal, NORMAL))
++    {
++      zlog_debug ("%s OPEN has Graceful Restart capability", peer->host);
++      zlog_debug ("%s Peer has%srestarted. Restart Time : %d",
++                  peer->host, restart_bit ? " " : " not ",
++                  peer->v_gr_restart);
++    }
+ 
+-      if (BGP_DEBUG (normal, NORMAL))
+-	zlog_debug ("%s OPEN has CAPABILITY code: %d, length %d",
+-		   peer->host, cap.code, cap.length);
++  while (stream_get_getp (s) + 4 < end)
++    {
++      afi_t afi = stream_getw (s);
++      safi_t safi = stream_getc (s);
++      u_char flag = stream_getc (s);
++      
++      if (!bgp_afi_safi_valid_indices (afi, &safi))
++        {
++          if (BGP_DEBUG (normal, NORMAL))
++            zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported."
++                        " Ignore the Graceful Restart capability",
++                        peer->host, afi, safi);
++        }
++      else if (!peer->afc[afi][safi])
++        {
++          if (BGP_DEBUG (normal, NORMAL))
++            zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled."
++                        " Ignore the Graceful Restart capability",
++                        peer->host, afi, safi);
++        }
++      else
++        {
++          if (BGP_DEBUG (normal, NORMAL))
++            zlog_debug ("%s Address family %s is%spreserved", peer->host,
++                        afi_safi_print (afi, safi),
++                        CHECK_FLAG (peer->af_cap[afi][safi],
++                                    PEER_CAP_RESTART_AF_PRESERVE_RCV)
++                        ? " " : " not ");
++
++          SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV);
++          if (CHECK_FLAG (flag, RESTART_F_BIT))
++            SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV);
++          
++        }
++    }
++  return 0;
++}
+ 
++static struct message capcode_str[] =
++{
++  { 0,	""},
++  { CAPABILITY_CODE_MP,			"MultiProtocol Extensions"	},
++  { CAPABILITY_CODE_REFRESH,		"Route Refresh"			},
++  { CAPABILITY_CODE_ORF,		"Cooperative Route Filtering" 	},
++  { CAPABILITY_CODE_RESTART,		"Graceful Restart"		},
++  { CAPABILITY_CODE_AS4,		"4-octet AS number"		},
++  { CAPABILITY_CODE_DYNAMIC,		"Dynamic"			},
++  { CAPABILITY_CODE_REFRESH_OLD,	"Route Refresh (Old)"		},
++  { CAPABILITY_CODE_ORF_OLD,		"ORF (Old)"			},
++};
++int capcode_str_max = sizeof(capcode_str)/sizeof(capcode_str[0]);
++
++/* Minimum sizes for length field of each cap (so not inc. the header) */
++static size_t cap_minsizes[] = 
++{
++  [CAPABILITY_CODE_MP]		= sizeof (struct capability_mp_data),
++  [CAPABILITY_CODE_REFRESH]	= CAPABILITY_CODE_REFRESH_LEN,
++  [CAPABILITY_CODE_ORF]		= sizeof (struct capability_orf_entry),
++  [CAPABILITY_CODE_RESTART]	= sizeof (struct capability_gr) - 2,
++  [CAPABILITY_CODE_AS4]		= CAPABILITY_CODE_AS4_LEN,
++  [CAPABILITY_CODE_DYNAMIC]	= CAPABILITY_CODE_DYNAMIC_LEN,
++  [CAPABILITY_CODE_REFRESH_OLD]	= CAPABILITY_CODE_REFRESH_LEN,
++  [CAPABILITY_CODE_ORF_OLD]	= sizeof (struct capability_orf_entry),
++};
++
++/* Parse given capability.
++ * XXX: This is reading into a stream, but not using stream API
++ */
++static int
++bgp_capability_parse (struct peer *peer, size_t length, u_char **error)
++{
++  int ret;
++  struct stream *s = BGP_INPUT (peer);
++  size_t end = stream_get_getp (s) + length;
++  
++  assert (STREAM_READABLE (s) >= length);
++  
++  while (stream_get_getp (s) < end)
++    {
++      size_t start;
++      u_char *sp = stream_pnt (s);
++      struct capability_header caphdr;
++      
+       /* We need at least capability code and capability length. */
+-      if (pnt + 2 > end)
++      if (stream_get_getp(s) + 2 > end)
+ 	{
+-	  zlog_info ("%s Capability length error", peer->host);
++	  zlog_info ("%s Capability length error (< header)", peer->host);
+ 	  bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ 	  return -1;
+ 	}
+-
+-      /* Capability length check. */
+-      if (pnt + (cap.length + 2) > end)
++      
++      caphdr.code = stream_getc (s);
++      caphdr.length = stream_getc (s);
++      start = stream_get_getp (s);
++      
++      /* Capability length check sanity check. */
++      if (start + caphdr.length > end)
+ 	{
+-	  zlog_info ("%s Capability length error", peer->host);
++	  zlog_info ("%s Capability length error (< length)", peer->host);
+ 	  bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+ 	  return -1;
+ 	}
+-
+-      /* We know MP Capability Code. */
+-      if (cap.code == CAPABILITY_CODE_MP)
+-	{
+-	  if (BGP_DEBUG (normal, NORMAL))
+-	    zlog_debug ("%s OPEN has MP_EXT CAP for afi/safi: %u/%u",
+-		       peer->host, afi, safi);
+-
+-	  /* Ignore capability when override-capability is set. */
+-	  if (! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+-	    {
+-	      /* Set negotiated value. */
+-	      ret = bgp_capability_mp (peer, &cap);
+-
+-	      /* Unsupported Capability. */
+-	      if (ret < 0)
+-		{
+-		  /* Store return data. */
+-		  memcpy (*error, &cap, cap.length + 2);
+-		  *error += cap.length + 2;
+-		}
+-	    }
+-	}
+-      else if (cap.code == CAPABILITY_CODE_REFRESH
+-	       || cap.code == CAPABILITY_CODE_REFRESH_OLD)
+-	{
+-	  /* Check length. */
+-	  if (cap.length != CAPABILITY_CODE_REFRESH_LEN)
+-	    {
+-	      zlog_info ("%s Route Refresh Capability length error %d",
+-			 peer->host, cap.length);
+-	      bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+-	      return -1;
+-	    }
+-
+-	  if (BGP_DEBUG (normal, NORMAL))
+-	    zlog_debug ("%s OPEN has ROUTE-REFRESH capability(%s) for all address-families",
+-		       peer->host,
+-		       cap.code == CAPABILITY_CODE_REFRESH_OLD ? "old" : "new");
+-
+-	  /* BGP refresh capability */
+-	  if (cap.code == CAPABILITY_CODE_REFRESH_OLD)
+-	    SET_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV);
+-	  else
+-	    SET_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV);
+-	}
+-      else if (cap.code == CAPABILITY_CODE_ORF
+-	       || cap.code == CAPABILITY_CODE_ORF_OLD)
+-	bgp_capability_orf (peer, &cap, pnt + sizeof (struct capability));
+-      else if (cap.code == CAPABILITY_CODE_RESTART)
+-       {
+-         struct graceful_restart_af graf;
+-         u_int16_t restart_flag_time;
+-         int restart_bit = 0;
+-         u_char *restart_pnt;
+-         u_char *restart_end;
+-
+-         /* Check length. */
+-         if (cap.length < CAPABILITY_CODE_RESTART_LEN)
+-           {
+-             zlog_info ("%s Graceful Restart Capability length error %d",
+-                        peer->host, cap.length);
+-             bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+-             return -1;
+-           }
+-
+-         SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV);
+-         restart_flag_time = ntohs(cap.mpc.afi);
+-         if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT))
+-           restart_bit = 1;
+-         UNSET_FLAG (restart_flag_time, 0xF000);
+-	 peer->v_gr_restart = restart_flag_time;
+-
+-         if (BGP_DEBUG (normal, NORMAL))
+-           {
+-             zlog_debug ("%s OPEN has Graceful Restart capability", peer->host);
+-             zlog_debug ("%s Peer has%srestarted. Restart Time : %d",
+-                        peer->host, restart_bit ? " " : " not ",
+-			peer->v_gr_restart);
+-           }
+-
+-         restart_pnt = pnt + 4;
+-         restart_end = pnt + cap.length + 2;
+-
+-         while (restart_pnt < restart_end)
+-           {
+-             memcpy (&graf, restart_pnt, sizeof (struct graceful_restart_af));
+-
+-             afi = ntohs(graf.afi);
+-             safi = graf.safi;
+-
+-             if (CHECK_FLAG (graf.flag, RESTART_F_BIT))
+-		SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV);
+-
+-             if (strcmp (afi_safi_print (afi, safi), "Unknown") == 0)
+-               {
+-                  if (BGP_DEBUG (normal, NORMAL))
+-                    zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported. I gnore the Graceful Restart capability",
+-                               peer->host, afi, safi);
+-               }
+-             else if (! peer->afc[afi][safi])
+-               {
+-                  if (BGP_DEBUG (normal, NORMAL))
+-                     zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled. Ignore the Graceful Restart capability",
+-                                peer->host, afi, safi);
+-               }
+-             else
+-               {
+-                 if (BGP_DEBUG (normal, NORMAL))
+-                   zlog_debug ("%s Address family %s is%spreserved", peer->host,
+-			       afi_safi_print (afi, safi),
+-			       CHECK_FLAG (peer->af_cap[afi][safi],
+-			       PEER_CAP_RESTART_AF_PRESERVE_RCV)
+-			       ? " " : " not ");
+-
+-                   SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV);
+-               }
+-             restart_pnt += 4;
+-           }
+-       }
+-      else if (cap.code == CAPABILITY_CODE_DYNAMIC)
+-	{
+-	  /* Check length. */
+-	  if (cap.length != CAPABILITY_CODE_DYNAMIC_LEN)
+-	    {
+-	      zlog_info ("%s Dynamic Capability length error %d",
+-			 peer->host, cap.length);
+-	      bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+-	      return -1;
+-	    }
+-
+-	  if (BGP_DEBUG (normal, NORMAL))
+-	    zlog_debug ("%s OPEN has DYNAMIC capability", peer->host);
+-
+-	  SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV);
+-	}
+- 
+-      else if (cap.code > 128)
+-	{
+-	  /* We don't send Notification for unknown vendor specific
+-	     capabilities.  It seems reasonable for now...  */
+-	  zlog_warn ("%s Vendor specific capability %d",
+-		     peer->host, cap.code);
+-	}
+-      else
+-	{
+-	  zlog_warn ("%s unrecognized capability code: %d - ignored",
+-		     peer->host, cap.code);
+-	  memcpy (*error, &cap, cap.length + 2);
+-	  *error += cap.length + 2;
+-	}
+-
+-      pnt += cap.length + 2;
++      
++      if (BGP_DEBUG (normal, NORMAL))
++	zlog_debug ("%s OPEN has %s capability (%u), length %u",
++		   peer->host,
++		   LOOKUP (capcode_str, caphdr.code),
++		   caphdr.code, caphdr.length);
++      
++      /* Length sanity check, type-specific, for known capabilities */
++      switch (caphdr.code)
++        {
++          case CAPABILITY_CODE_MP:
++          case CAPABILITY_CODE_REFRESH:
++          case CAPABILITY_CODE_REFRESH_OLD:
++          case CAPABILITY_CODE_ORF:
++          case CAPABILITY_CODE_ORF_OLD:
++          case CAPABILITY_CODE_RESTART:
++          case CAPABILITY_CODE_DYNAMIC:
++              /* Check length. */
++              if (caphdr.length < cap_minsizes[caphdr.code])
++                {
++                  zlog_info ("%s %s Capability length error: got %u,"
++                             " expected at least %u",
++                             peer->host, 
++                             LOOKUP (capcode_str, caphdr.code),
++                             caphdr.length, cap_minsizes[caphdr.code]);
++                  bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
++                  return -1;
++                }
++          /* we deliberately ignore unknown codes, see below */
++          default:
++            break;
++        }
++      
++      switch (caphdr.code)
++        {
++          case CAPABILITY_CODE_MP:
++            {
++              /* Ignore capability when override-capability is set. */
++              if (! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
++                {
++                  /* Set negotiated value. */
++                  ret = bgp_capability_mp (peer, &caphdr);
++
++                  /* Unsupported Capability. */
++                  if (ret < 0)
++                    {
++                      /* Store return data. */
++                      memcpy (*error, sp, caphdr.length + 2);
++                      *error += caphdr.length + 2;
++                    }
++                }
++            }
++            break;
++          case CAPABILITY_CODE_REFRESH:
++          case CAPABILITY_CODE_REFRESH_OLD:
++            {
++              /* BGP refresh capability */
++              if (caphdr.code == CAPABILITY_CODE_REFRESH_OLD)
++                SET_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV);
++              else
++                SET_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV);
++            }
++            break;
++          case CAPABILITY_CODE_ORF:
++          case CAPABILITY_CODE_ORF_OLD:
++            if (bgp_capability_orf (peer, &caphdr))
++              return -1;
++            break;
++          case CAPABILITY_CODE_RESTART:
++            if (bgp_capability_restart (peer, &caphdr))
++              return -1;
++            break;
++          case CAPABILITY_CODE_DYNAMIC:
++            SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV);
++            break;
++          default:
++            if (caphdr.code > 128)
++              {
++                /* We don't send Notification for unknown vendor specific
++                   capabilities.  It seems reasonable for now...  */
++                zlog_warn ("%s Vendor specific capability %d",
++                           peer->host, caphdr.code);
++              }
++            else
++              {
++                zlog_warn ("%s unrecognized capability code: %d - ignored",
++                           peer->host, caphdr.code);
++                memcpy (*error, sp, caphdr.length + 2);
++                *error += caphdr.length + 2;
++              }
++          }
++      if (stream_get_getp(s) != (start + caphdr.length))
++        {
++          if (stream_get_getp(s) > (start + caphdr.length))
++            zlog_warn ("%s Cap-parser for %s read past cap-length, %u!",
++                       peer->host, LOOKUP (capcode_str, caphdr.code),
++                       caphdr.length);
++          stream_set_getp (s, start + caphdr.length);
++        }
+     }
+   return 0;
+ }
+ 
+ static int
+-bgp_auth_parse (struct peer *peer, u_char *pnt, size_t length)
++bgp_auth_parse (struct peer *peer, size_t length)
+ {
+   bgp_notify_send (peer, 
+ 		   BGP_NOTIFY_OPEN_ERR, 
+@@ -530,30 +620,25 @@ int
+ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
+ {
+   int ret;
+-  u_char *end;
+-  u_char opt_type;
+-  u_char opt_length;
+-  u_char *pnt;
+   u_char *error;
+   u_char error_data[BGP_MAX_PACKET_SIZE];
+-
+-  /* Fetch pointer. */
+-  pnt = stream_pnt (peer->ibuf);
++  struct stream *s = BGP_INPUT(peer);
++  size_t end = stream_get_getp (s) + length;
+ 
+   ret = 0;
+-  opt_type = 0;
+-  opt_length = 0;
+-  end = pnt + length;
+   error = error_data;
+ 
+   if (BGP_DEBUG (normal, NORMAL))
+     zlog_debug ("%s rcv OPEN w/ OPTION parameter len: %u",
+ 	       peer->host, length);
+   
+-  while (pnt < end) 
++  while (stream_get_getp(s) < end)
+     {
+-      /* Check the length. */
+-      if (pnt + 2 > end)
++      u_char opt_type;
++      u_char opt_length;
++      
++      /* Must have at least an OPEN option header */
++      if (STREAM_READABLE(s) < 2)
+ 	{
+ 	  zlog_info ("%s Option length error", peer->host);
+ 	  bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+@@ -561,11 +646,11 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
+ 	}
+ 
+       /* Fetch option type and length. */
+-      opt_type = *pnt++;
+-      opt_length = *pnt++;
++      opt_type = stream_getc (s);
++      opt_length = stream_getc (s);
+       
+       /* Option length check. */
+-      if (pnt + opt_length > end)
++      if (STREAM_READABLE (s) < opt_length)
+ 	{
+ 	  zlog_info ("%s Option length error", peer->host);
+ 	  bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+@@ -582,10 +667,10 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
+       switch (opt_type)
+ 	{
+ 	case BGP_OPEN_OPT_AUTH:
+-	  ret = bgp_auth_parse (peer, pnt, opt_length);
++	  ret = bgp_auth_parse (peer, opt_length);
+ 	  break;
+ 	case BGP_OPEN_OPT_CAP:
+-	  ret = bgp_capability_parse (peer, pnt, opt_length, &error);
++	  ret = bgp_capability_parse (peer, opt_length, &error);
+ 	  *capability = 1;
+ 	  break;
+ 	default:
+@@ -602,9 +687,6 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability)
+          error and erro_data pointer, like below.  */
+       if (ret < 0)
+ 	return -1;
+-
+-      /* Forward pointer. */
+-      pnt += opt_length;
+     }
+ 
+   /* All OPEN option is parsed.  Check capability when strict compare
+diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
+index 7515d3f..436eb01 100644
+--- bgpd/bgp_open.h
++++ bgpd/bgp_open.h
+@@ -21,21 +21,32 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ #ifndef _QUAGGA_BGP_OPEN_H
+ #define _QUAGGA_BGP_OPEN_H
+ 
+-/* MP Capability information. */
+-struct capability_mp
++/* Standard header for capability TLV */
++struct capability_header
++{
++  u_char code;
++  u_char length;
++};
++
++/* Generic MP capability data */
++struct capability_mp_data
+ {
+   u_int16_t afi;
+   u_char reserved;
+   u_char safi;
+ };
+ 
+-/* BGP open message capability. */
+-struct capability
++#pragma pack(1)
++struct capability_orf_entry 
+ {
+-  u_char code;
+-  u_char length;
+-  struct capability_mp mpc;
+-};
++  struct capability_mp_data mpc;
++  u_char num;
++  struct {
++    u_char type;
++    u_char mode;
++  } orfs[];
++} __attribute__ ((packed));
++#pragma pack()
+ 
+ struct graceful_restart_af
+ {
+@@ -44,12 +55,18 @@ struct graceful_restart_af
+   u_char flag;
+ };
+ 
++struct capability_gr
++{
++  u_int16_t restart_flag_time;
++  struct graceful_restart_af gr[];
++};
++
+ /* Capability Code */
+ #define CAPABILITY_CODE_MP              1 /* Multiprotocol Extensions */
+ #define CAPABILITY_CODE_REFRESH         2 /* Route Refresh Capability */
+ #define CAPABILITY_CODE_ORF             3 /* Cooperative Route Filtering Capability */
+ #define CAPABILITY_CODE_RESTART        64 /* Graceful Restart Capability */
+-#define CAPABILITY_CODE_4BYTE_AS       65 /* 4-octet AS number Capability */
++#define CAPABILITY_CODE_AS4            65 /* 4-octet AS number Capability */
+ #define CAPABILITY_CODE_DYNAMIC        66 /* Dynamic Capability */
+ #define CAPABILITY_CODE_REFRESH_OLD   128 /* Route Refresh Capability(cisco) */
+ #define CAPABILITY_CODE_ORF_OLD       130 /* Cooperative Route Filtering Capability(cisco) */
+@@ -59,6 +76,7 @@ struct graceful_restart_af
+ #define CAPABILITY_CODE_REFRESH_LEN     0
+ #define CAPABILITY_CODE_DYNAMIC_LEN     0
+ #define CAPABILITY_CODE_RESTART_LEN     2 /* Receiving only case */
++#define CAPABILITY_CODE_AS4_LEN         4
+ 
+ /* Cooperative Route Filtering Capability.  */
+ 
+@@ -82,5 +100,6 @@ struct graceful_restart_af
+ extern int bgp_open_option_parse (struct peer *, u_char, int *);
+ extern void bgp_open_capability (struct stream *, struct peer *);
+ extern void bgp_capability_vty_out (struct vty *, struct peer *);
++extern int bgp_afi_safi_valid_indices (afi_t, safi_t *);
+ 
+ #endif /* _QUAGGA_BGP_OPEN_H */
+diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
+index 2653201..17ac1f7 100644
+--- bgpd/bgp_packet.c
++++ bgpd/bgp_packet.c
+@@ -1371,8 +1371,6 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
+       ret = bgp_open_option_parse (peer, optlen, &capability);
+       if (ret < 0)
+ 	return ret;
+-
+-      stream_forward_getp (peer->ibuf, optlen);
+     }
+   else
+     {
+@@ -1991,7 +1989,8 @@ static int
+ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
+ {
+   u_char *end;
+-  struct capability cap;
++  struct capability_mp_data mpc;
++  struct capability_header *hdr;
+   u_char action;
+   struct bgp *bgp;
+   afi_t afi;
+@@ -2001,7 +2000,7 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
+   end = pnt + length;
+ 
+   while (pnt < end)
+-    {
++    {      
+       /* We need at least action, capability code and capability length. */
+       if (pnt + 3 > end)
+         {
+@@ -2009,12 +2008,9 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
+           bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+           return -1;
+         }
+-
+       action = *pnt;
+-
+-      /* Fetch structure to the byte stream. */
+-      memcpy (&cap, pnt + 1, sizeof (struct capability));
+-
++      hdr = (struct capability_header *)(pnt + 1);
++      
+       /* Action value check.  */
+       if (action != CAPABILITY_ACTION_SET
+ 	  && action != CAPABILITY_ACTION_UNSET)
+@@ -2027,77 +2023,77 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
+ 
+       if (BGP_DEBUG (normal, NORMAL))
+ 	zlog_debug ("%s CAPABILITY has action: %d, code: %u, length %u",
+-		   peer->host, action, cap.code, cap.length);
++		   peer->host, action, hdr->code, hdr->length);
+ 
+       /* Capability length check. */
+-      if (pnt + (cap.length + 3) > end)
++      if ((pnt + hdr->length + 3) > end)
+         {
+           zlog_info ("%s Capability length error", peer->host);
+           bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
+           return -1;
+         }
+ 
++      /* Fetch structure to the byte stream. */
++      memcpy (&mpc, pnt + 3, sizeof (struct capability_mp_data));
++
+       /* We know MP Capability Code. */
+-      if (cap.code == CAPABILITY_CODE_MP)
++      if (hdr->code == CAPABILITY_CODE_MP)
+         {
+-	  afi = ntohs (cap.mpc.afi);
+-	  safi = cap.mpc.safi;
++	  afi = ntohs (mpc.afi);
++	  safi = mpc.safi;
+ 
+           /* Ignore capability when override-capability is set. */
+           if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
+ 	    continue;
+-
++          
++          if (!bgp_afi_safi_valid_indices (afi, &safi))
++            {
++              if (BGP_DEBUG (normal, NORMAL))
++                zlog_debug ("%s Dynamic Capability MP_EXT afi/safi invalid",
++                            peer->host, afi, safi);
++              continue;
++            }
++          
+ 	  /* Address family check.  */
+-	  if ((afi == AFI_IP 
+-	       || afi == AFI_IP6)
+-	      && (safi == SAFI_UNICAST 
+-		  || safi == SAFI_MULTICAST 
+-		  || safi == BGP_SAFI_VPNV4))
+-	    {
+-	      if (BGP_DEBUG (normal, NORMAL))
+-		zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u",
+-			   peer->host,
+-			   action == CAPABILITY_ACTION_SET 
+-			   ? "Advertising" : "Removing",
+-			   ntohs(cap.mpc.afi) , cap.mpc.safi);
+-		  
+-	      /* Adjust safi code. */
+-	      if (safi == BGP_SAFI_VPNV4)
+-		safi = SAFI_MPLS_VPN;
+-	      
+-	      if (action == CAPABILITY_ACTION_SET)
+-		{
+-		  peer->afc_recv[afi][safi] = 1;
+-		  if (peer->afc[afi][safi])
+-		    {
+-		      peer->afc_nego[afi][safi] = 1;
+-		      bgp_announce_route (peer, afi, safi);
+-		    }
+-		}
+-	      else
+-		{
+-		  peer->afc_recv[afi][safi] = 0;
+-		  peer->afc_nego[afi][safi] = 0;
+-
+-		  if (peer_active_nego (peer))
+-		    bgp_clear_route (peer, afi, safi);
+-		  else
+-		    BGP_EVENT_ADD (peer, BGP_Stop);
+-		} 
+-	    }
++          if (BGP_DEBUG (normal, NORMAL))
++            zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u",
++                       peer->host,
++                       action == CAPABILITY_ACTION_SET 
++                       ? "Advertising" : "Removing",
++                       ntohs(mpc.afi) , mpc.safi);
++              
++          if (action == CAPABILITY_ACTION_SET)
++            {
++              peer->afc_recv[afi][safi] = 1;
++              if (peer->afc[afi][safi])
++                {
++                  peer->afc_nego[afi][safi] = 1;
++                  bgp_announce_route (peer, afi, safi);
++                }
++            }
++          else
++            {
++              peer->afc_recv[afi][safi] = 0;
++              peer->afc_nego[afi][safi] = 0;
++
++              if (peer_active_nego (peer))
++                bgp_clear_route (peer, afi, safi);
++              else
++                BGP_EVENT_ADD (peer, BGP_Stop);
++            }
+         }
+       else
+         {
+           zlog_warn ("%s unrecognized capability code: %d - ignored",
+-                     peer->host, cap.code);
++                     peer->host, hdr->code);
+         }
+-      pnt += cap.length + 3;
++      pnt += hdr->length + 3;
+     }
+   return 0;
+ }
+ 
+ /* Dynamic Capability is received. */
+-static void
++int
+ bgp_capability_receive (struct peer *peer, bgp_size_t size)
+ {
+   u_char *pnt;
+@@ -2130,7 +2126,7 @@ bgp_capability_receive (struct peer *peer, bgp_size_t size)
+     }
+ 
+   /* Parse packet. */
+-  ret = bgp_capability_msg_parse (peer, pnt, size);
++  return bgp_capability_msg_parse (peer, pnt, size);
+ }
+ 
+ /* BGP read utility function. */
+diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
+index 1e21c74..3eeb5f9 100644
+--- bgpd/bgp_vty.c
++++ bgpd/bgp_vty.c
+@@ -6681,14 +6681,14 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi)
+ 
+ 	  vty_out (vty, "4 ");
+ 
+-	  vty_out (vty, "%5d %7d %7d %8d %4d %4ld ",
++	  vty_out (vty, "%5d %7d %7d %8d %4d %4lu ",
+ 		   peer->as,
+ 		   peer->open_in + peer->update_in + peer->keepalive_in
+ 		   + peer->notify_in + peer->refresh_in + peer->dynamic_cap_in,
+ 		   peer->open_out + peer->update_out + peer->keepalive_out
+ 		   + peer->notify_out + peer->refresh_out
+ 		   + peer->dynamic_cap_out,
+-		   0, 0, peer->obuf->count);
++		   0, 0, (unsigned long)peer->obuf->count);
+ 
+ 	  vty_out (vty, "%8s", 
+ 		   peer_uptime (peer->uptime, timebuf, BGP_UPTIME_LEN));
+@@ -7403,7 +7403,7 @@ bgp_show_peer (struct vty *vty, struct peer *p)
+   /* Packet counts. */
+   vty_out (vty, "  Message statistics:%s", VTY_NEWLINE);
+   vty_out (vty, "    Inq depth is 0%s", VTY_NEWLINE);
+-  vty_out (vty, "    Outq depth is %ld%s", p->obuf->count, VTY_NEWLINE);
++  vty_out (vty, "    Outq depth is %lu%s", (unsigned long)p->obuf->count, VTY_NEWLINE);
+   vty_out (vty, "                         Sent       Rcvd%s", VTY_NEWLINE);
+   vty_out (vty, "    Opens:         %10d %10d%s", p->open_out, p->open_in, VTY_NEWLINE);
+   vty_out (vty, "    Notifications: %10d %10d%s", p->notify_out, p->notify_in, VTY_NEWLINE);
+diff --git a/lib/log.c b/lib/log.c
+index cbf76af..ff47cae 100644
+--- lib/log.c
++++ lib/log.c
+@@ -769,7 +769,7 @@ mes_lookup (struct message *meslist, int max, int index)
+       {
+ 	if (meslist->key == index)
+ 	  {
+-	    zlog_warn("message index %d [%s] found in position %d (max is %d)",
++	    zlog_debug ("message index %d [%s] found in position %d (max is %d)",
+ 		      index, meslist->str, i, max);
+ 	    return meslist->str;
+ 	  }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/45-bgpd-capab-typo.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,27 @@
+commit 7b87b33fa53254f14b79e95bb8a0b24f8ef9e1c1
+Author: Paul Jakma <[email protected]>
+Date:   Mon Sep 17 13:51:28 2007 +0100
+
+    [bgpd] Fix typo, which prevented advertisement of MP (non-IPv4) prefixes
+    
+    2007-09-17 Paul Jakma <[email protected]>
+    
+    	* bgp_open.c: (bgp_capability_mp) We were setting
+    	  afc_nego[safi][safi] rather than afc_nego[afi][safi], thus
+    	  failling to announce any non-IPv4 prefixes. Remove the extra,
+    	  typo-ed character.
+    	* tests/bgp_capability_test.c: Test that peer's adv_recv and
+    	  adv_nego get set correctly for MP capability and given AFI/SAFI.
+    	  Colour OK/failed result so it's easier to find them.
+
+--- bgpd/bgp_open.c
++++ bgpd/bgp_open.c
+@@ -177,7 +177,7 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr)
+   peer->afc_recv[mpc.afi][mpc.safi] = 1;
+   
+   if (peer->afc[mpc.afi][mpc.safi])
+-    peer->afc_nego[mpc.safi][mpc.safi] = 1;
++    peer->afc_nego[mpc.afi][mpc.safi] = 1;
+   else 
+     return -1;
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/50-bgpd-nosub.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,27 @@
+commit 6d134fb4defecb16591adbf4acb020acd165a75a
+Author: Paul Jakma <[email protected]>
+Date:   Thu Aug 23 23:22:02 2007 +0000
+
+    [bgpd] Pass NOSUB to regexec
+    
+    2007-08-23 Paul Jakma <[email protected]>
+    
+    	* bgp_regex.c: (bgp_regcomp) Pass NOSUB flag to regcomp to
+    	  prevent parsing of substitutions, which can have profound
+    	  performance effects on bgpd and are of no use to the CLI
+    	  anyway. How much it helps depends on the regex
+    	  implementation.
+
+diff --git a/bgpd/bgp_regex.c b/bgpd/bgp_regex.c
+index be84d40..9b65f7c 100644
+--- bgpd/bgp_regex.c
++++ bgpd/bgp_regex.c
+@@ -66,7 +66,7 @@ bgp_regcomp (const char *regstr)
+ 
+   regex = XMALLOC (MTYPE_BGP_REGEXP, sizeof (regex_t));
+ 
+-  ret = regcomp (regex, magic_str, REG_EXTENDED);
++  ret = regcomp (regex, magic_str, REG_EXTENDED|REG_NOSUB);
+ 
+   XFREE (MTYPE_TMP, magic_str);
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/55-bgpd-rm-assert.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,217 @@
+commit 9eda90ce8094683a5315007fbd0f9249a284f36f
+Author: Paul Jakma <[email protected]>
+Date:   Thu Aug 30 13:36:17 2007 +0000
+
+    [bgpd] bug #398 Bogus free on out route-map, and assert() with rsclients
+    
+    2007-08-27 Paul Jakma <[email protected]>
+    
+    	* bgp_route.c: (bgp_announce_check) Fix bug #398, slight
+    	  modification of Vladimir Ivanov's suggested fix - to keep
+    	  memory alloc conditional.
+    	  (bgp_process_announce_selected) Don't take struct attr as
+    	  argument, none of the callers need it and it needlessly
+    	  distances allocation from use.
+    	  Free the extended attr, the attr itself is on the stack.
+    	  Fix bad indentation.
+    	* bgp_attr.c: (bgp_packet_attribute) Remove incorrect assert,
+    	  and adjust conditional to test attr->extra, diagnosis by
+    	  Vladimir Ivanov in bug #398.
+    
+    2007-08-27 Vladimir Ivanov <[email protected]>
+    
+    	* bgp_route.c: (bgp_announce_check_rsclient) copy of
+    	  ri->attr is no longer deep enough, due to addition of
+    	  attr->extra. It should use bgp_attr_dup, as
+    	  bgp_announce_check() does.
+
+diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
+index 23d9586..ee17b6d 100644
+--- bgpd/bgp_attr.c
++++ bgpd/bgp_attr.c
+@@ -1625,8 +1625,6 @@
+       && from
+       && peer_sort (from) == BGP_PEER_IBGP)
+     {
+-      assert (attr->extra);
+-      
+       /* Originator ID. */
+       stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+       stream_putc (s, BGP_ATTR_ORIGINATOR_ID);
+@@ -1641,7 +1639,7 @@
+       stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+       stream_putc (s, BGP_ATTR_CLUSTER_LIST);
+       
+-      if (attr->extra->cluster)
++      if (attr->extra && attr->extra->cluster)
+ 	{
+ 	  stream_putc (s, attr->extra->cluster->length + 4);
+ 	  /* If this peer configuration's parent BGP has cluster_id. */
+diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
+index 0f4da98..9ddeca5 100644
+--- bgpd/bgp_route.c
++++ bgpd/bgp_route.c
+@@ -999,11 +999,10 @@
+       || (ri->extra && ri->extra->suppress) )
+     {
+       struct bgp_info info;
+-      struct attr dummy_attr;
++      struct attr dummy_attr = { 0 };
+       
+       info.peer = peer;
+       info.attr = attr;
+-      
+ 
+       /* The route reflector is not allowed to modify the attributes
+ 	 of the reflected IBGP routes. */
+@@ -1010,9 +1009,8 @@
+       if (peer_sort (from) == BGP_PEER_IBGP 
+ 	  && peer_sort (peer) == BGP_PEER_IBGP)
+ 	{
+-	  dummy_attr.extra = NULL;
+ 	  bgp_attr_dup (&dummy_attr, attr);
+-	  info.attr = &dummy_attr; 
++	  info.attr = &dummy_attr;
+ 	}
+ 
+       SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT); 
+@@ -1024,7 +1022,8 @@
+ 
+       peer->rmap_type = 0;
+       
+-      bgp_attr_extra_free (&dummy_attr);
++      if (dummy_attr.extra)
++        bgp_attr_extra_free (&dummy_attr);
+       
+       if (ret == RMAP_DENYMATCH)
+ 	{
+@@ -1127,7 +1126,7 @@
+ #endif /* BGP_SEND_ASPATH_CHECK */
+ 
+   /* For modify attribute, copy it to temporary structure. */
+-  *attr = *ri->attr;
++  bgp_attr_dup (attr, ri->attr);
+ 
+   /* next-hop-set */
+   if ((p->family == AF_INET && attr->nexthop.s_addr == 0)
+@@ -1329,21 +1328,22 @@
+ 
+ static int
+ bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected,
+-        struct bgp_node *rn, struct attr *attr, afi_t afi, safi_t safi)
+-    {
++                               struct bgp_node *rn, afi_t afi, safi_t safi)
++{
+   struct prefix *p;
++  struct attr attr = { 0 };
+ 
+   p = &rn->p;
+ 
+-      /* Announce route to Established peer. */
+-      if (peer->status != Established)
++  /* Announce route to Established peer. */
++  if (peer->status != Established)
+     return 0;
+ 
+-      /* Address family configuration check. */
+-      if (! peer->afc_nego[afi][safi])
++  /* Address family configuration check. */
++  if (! peer->afc_nego[afi][safi])
+     return 0;
+ 
+-      /* First update is deferred until ORF or ROUTE-REFRESH is received */
++  /* First update is deferred until ORF or ROUTE-REFRESH is received */
+   if (CHECK_FLAG (peer->af_sflags[afi][safi],
+       PEER_STATUS_ORF_WAIT_REFRESH))
+     return 0;
+@@ -1353,8 +1353,8 @@
+       case BGP_TABLE_MAIN:
+       /* Announcement to peer->conf.  If the route is filtered,
+          withdraw it. */
+-        if (selected && bgp_announce_check (selected, peer, p, attr, afi, safi))
+-          bgp_adj_out_set (rn, peer, p, attr, afi, safi, selected);
++        if (selected && bgp_announce_check (selected, peer, p, &attr, afi, safi))
++          bgp_adj_out_set (rn, peer, p, &attr, afi, safi, selected);
+         else
+           bgp_adj_out_unset (rn, peer, p, afi, safi);
+         break;
+@@ -1361,13 +1361,16 @@
+       case BGP_TABLE_RSCLIENT:
+         /* Announcement to peer->conf.  If the route is filtered, 
+            withdraw it. */
+-        if (selected && bgp_announce_check_rsclient
+-              (selected, peer, p, attr, afi, safi))
+-          bgp_adj_out_set (rn, peer, p, attr, afi, safi, selected);
+-      else
+-	bgp_adj_out_unset (rn, peer, p, afi, safi);
++        if (selected && 
++            bgp_announce_check_rsclient (selected, peer, p, &attr, afi, safi))
++          bgp_adj_out_set (rn, peer, p, &attr, afi, safi, selected);
++        else
++	  bgp_adj_out_unset (rn, peer, p, afi, safi);
+         break;
+     }
++  
++  bgp_attr_extra_free (&attr);
++  
+   return 0;
+ }
+ 
+@@ -1417,8 +1420,7 @@
+ 	      bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED);
+ 	    }
+ 
+-	  bgp_process_announce_selected (rsclient, new_select, rn, &attr,
+-					 afi, safi);
++	  bgp_process_announce_selected (rsclient, new_select, rn, afi, safi);
+ 	}
+     }
+   else
+@@ -1430,8 +1432,7 @@
+ 	  bgp_info_set_flag (rn, new_select, BGP_INFO_SELECTED);
+ 	  bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED);
+ 	}
+-      bgp_process_announce_selected (rsclient, new_select, rn,
+-				     &attr, afi, safi);
++      bgp_process_announce_selected (rsclient, new_select, rn, afi, safi);
+     }
+ 
+   if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED))
+@@ -1457,10 +1458,7 @@
+   struct bgp_info_pair old_and_new;
+   struct listnode *node, *nnode;
+   struct peer *peer;
+-  struct attr attr;
+   
+-  memset (&attr, 0, sizeof (struct attr));
+-  
+   /* Best path selection. */
+   bgp_best_selection (bgp, rn, &old_and_new);
+   old_select = old_and_new.old;
+@@ -1491,7 +1489,7 @@
+   /* Check each BGP peer. */
+   for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+     {
+-      bgp_process_announce_selected (peer, new_select, rn, &attr, afi, safi);
++      bgp_process_announce_selected (peer, new_select, rn, afi, safi);
+     }
+ 
+   /* FIB update. */
+@@ -1516,8 +1514,6 @@
+   if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED))
+     bgp_info_reap (rn, old_select);
+   
+-  bgp_attr_extra_free (&attr);
+-  
+   UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
+   return WQ_SUCCESS;
+ }
+@@ -5888,7 +5884,7 @@
+ 	      {
+ 		struct route_map *rmap = output_arg;
+ 		struct bgp_info binfo;
+-		struct attr dummy_attr; 
++		struct attr dummy_attr = { 0 }; 
+ 		int ret;
+ 
+ 		bgp_attr_dup (&dummy_attr, ri->attr);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/60-bgp-comm-crash.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,53 @@
+Author: Paul Jakma <[email protected]>
+Date:   Fri Sep 7 14:24:55 2007 +0000
+
+    [bgpd] low-impact DoS: crash on malformed community with debug set
+    
+    2007-09-07 Paul Jakma <[email protected]>
+    
+    	* (general) bgpd can be made crash by remote peers if debug
+    	  bgp updates is set, due to NULL pointer dereference.
+    	  Reported by "Mu Security Research Team",
+    	  <[email protected]>.
+    	* bgp_attr.c: (bgp_attr_community) If community length is 0,
+    	  don't set the community-present attribute bit, just return
+    	  early.
+    	* bgp_debug.c: (community_str,community_com2str) Check com
+    	  pointer before dereferencing.
+
+--- bgpd/bgp_attr.c
++++ bgpd/bgp_attr.c
+@@ -962,7 +962,10 @@
+ 		    struct attr *attr, u_char flag)
+ {
+   if (length == 0)
+-    attr->community = NULL;
++    {
++      attr->community = NULL;
++      return 0;
++    }
+   else
+     {
+       attr->community = 
+--- bgpd/bgp_community.c
++++ bgpd/bgp_community.c
+@@ -206,6 +206,9 @@ community_com2str  (struct community *com)
+   u_int16_t as;
+   u_int16_t val;
+ 
++  if (!com)
++    return NULL;
++  
+   /* When communities attribute is empty.  */
+   if (com->size == 0)
+     {
+@@ -377,6 +380,9 @@ community_dup (struct community *com)
+ char *
+ community_str (struct community *com)
+ {
++  if (!com)
++    return NULL;
++  
+   if (! com->str)
+     com->str = community_com2str (com);
+   return com->str;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/65-isisd-iso-checksum.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,676 @@
+diff -ur quagga-0.99.8/ChangeLog quagga-unified-checksum/ChangeLog
+--- ChangeLog
++++ ChangeLog
+@@ -1,3 +1,18 @@
++2008-06-25 Jingjing Duan <[email protected]>
++	* lib/checksum.[ch]: Add a consolidated checksum function to be
++	  used by both OSPF and IS-IS. The consolidated version also fixes
++	  the checksum byte-order checksum on big-endian architectures.
++	* isisd/iso_checksum.[ch]: Remove the old checksum implementation
++	  and use the consolidated version.
++	* isisd/isis_dlpi.c: Change ioctl from PFIOCSETF (transparent mode)
++	  to I_STR (non-transparent mode). The old code resulted in no
++	  filtering at all. 
++	* isisd/isis_dlpi.c: (open_dlpi_dev) Clearview-UV device nodes are
++	  under /dev/net, try opening there before attempting style 1 or 2
++	  names.
++	* ospfd/ospf_lsa.c: Remove the old checksum implementation and
++	  use the consolidated version.
++
+ 2007-08-07 James Carlson <[email protected]>
+ 
+ 	* configure.ac: Added support for separate link-layer access
+diff -ur quagga-0.99.8/isisd/isis_dlpi.c quagga-unified-checksum/isisd/isis_dlpi.c
+--- isisd/isis_dlpi.c
++++ isisd/isis_dlpi.c
+@@ -42,8 +42,6 @@
+ #include "isisd/isis_circuit.h"
+ #include "isisd/isis_flags.h"
+ #include "isisd/isisd.h"
+-#include "isisd/isis_constants.h"
+-#include "isisd/isis_circuit.h"
+ #include "isisd/isis_network.h"
+ 
+ #include "privs.h"
+@@ -315,13 +313,24 @@
+ 	circuit->interface->name);
+       return ISIS_WARNING;
+     }
++  
++  /* 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);
++    }
++  
++  /* Now try as an ordinary Style 1 node */
++  if (fd == -1)
++    {
++      (void) snprintf (devpath, sizeof (devpath), "/dev/%s",
++                      circuit->interface->name);
++      unit = -1;
++      fd = dlpiopen (devpath, &acklen);
++    }
+ 
+-  /* Try first as Style 1 */
+-  (void) snprintf(devpath, sizeof (devpath), "/dev/%s",
+-    circuit->interface->name);
+-  unit = -1;
+-  fd = dlpiopen (devpath, &acklen);
+-
+   /* If that fails, try again as Style 2 */
+   if (fd == -1)
+     {
+@@ -452,11 +461,19 @@
+   if (ioctl (fd, I_PUSH, "pfmod") == 0)
+     {
+       struct packetfilt pfil;
++      struct strioctl sioc;
+ 
+       pfil.Pf_Priority = 0;
+       pfil.Pf_FilterLen = sizeof (pf_filter) / sizeof (u_short);
+       memcpy (pfil.Pf_Filter, pf_filter, sizeof (pf_filter));
+-      ioctl (fd, PFIOCSETF, &pfil);
++      /* pfmod does not support transparent ioctls */
++      sioc.ic_cmd = PFIOCSETF;
++      sioc.ic_timout = 5;
++      sioc.ic_len = sizeof (struct packetfilt);
++      sioc.ic_dp = (char *)&pfil;
++      if (ioctl (fd, I_STR, &sioc) == -1)
++         zlog_warn("%s: could not perform PF_IOCSETF on %s",
++           __func__, circuit->interface->name);
+     }
+ 
+   circuit->fd = fd;
+diff -ur quagga-0.99.8/isisd/isis_lsp.c quagga-unified-checksum/isisd/isis_lsp.c
+--- isisd/isis_lsp.c
++++ isisd/isis_lsp.c
+@@ -33,6 +33,7 @@
+ #include "command.h"
+ #include "hash.h"
+ #include "if.h"
++#include "checksum.h"
+ 
+ #include "isisd/dict.h"
+ #include "isisd/isis_constants.h"
+@@ -45,7 +46,6 @@
+ #include "isisd/isis_dynhn.h"
+ #include "isisd/isis_misc.h"
+ #include "isisd/isis_flags.h"
+-#include "isisd/iso_checksum.h"
+ #include "isisd/isis_csm.h"
+ #include "isisd/isis_adjacency.h"
+ #include "isisd/isis_spf.h"
+@@ -314,7 +314,7 @@
+     newseq = seq_num++;
+ 
+   lsp->lsp_header->seq_num = htonl (newseq);
+-  iso_csum_create (STREAM_DATA (lsp->pdu) + 12,
++  fletcher_checksum (STREAM_DATA (lsp->pdu) + 12,
+ 		   ntohs (lsp->lsp_header->pdu_len) - 12, 12);
+ 
+   return;
+@@ -1803,7 +1803,7 @@
+     tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu);
+ 
+   lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
+-  iso_csum_create (STREAM_DATA (lsp->pdu) + 12,
++  fletcher_checksum (STREAM_DATA (lsp->pdu) + 12,
+ 		   ntohs (lsp->lsp_header->pdu_len) - 12, 12);
+ 
+   list_delete (adj_list);
+@@ -2071,7 +2071,7 @@
+       lsp->lsp_header->pdu_len =
+ 	htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+       lsp->purged = 0;
+-      iso_csum_create (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);
+     }
+diff -ur quagga-0.99.8/isisd/isis_pdu.c quagga-unified-checksum/isisd/isis_pdu.c
+--- isisd/isis_pdu.c
++++ isisd/isis_pdu.c
+@@ -32,6 +32,7 @@
+ #include "hash.c"
+ #include "prefix.h"
+ #include "if.h"
++#include "checksum.h"
+ 
+ #include "isisd/dict.h"
+ #include "isisd/include-netbsd/iso.h"
+@@ -1121,7 +1122,7 @@
+ 		  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+ 		    zlog_debug ("LSP LEN: %d",
+ 				ntohs (lsp->lsp_header->pdu_len));
+-		  iso_csum_create (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);
+ 		  if (isis->debugs & DEBUG_UPDATE_PACKETS)
+@@ -1164,7 +1165,7 @@
+ 	  /* 7.3.16.1  */
+ 	  lsp->lsp_header->seq_num = htonl (ntohl (hdr->seq_num) + 1);
+ 
+-	  iso_csum_create (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);
+diff -ur quagga-0.99.8/isisd/iso_checksum.c quagga-unified-checksum/isisd/iso_checksum.c
+--- isisd/iso_checksum.c
++++ isisd/iso_checksum.c
+@@ -23,6 +23,7 @@
+ 
+ #include <zebra.h>
+ #include "iso_checksum.h"
++#include "checksum.h"
+ 
+ /*
+  * Calculations of the OSI checksum.
+@@ -47,14 +48,10 @@
+ int
+ iso_csum_verify (u_char * buffer, int len, uint16_t * csum)
+ {
+-  u_int8_t *p;
++  u_int16_t checksum;
+   u_int32_t c0;
+   u_int32_t c1;
+-  u_int16_t checksum;
+-  int i, partial_len;
+ 
+-  p = buffer;
+-  checksum = 0;
+   c0 = *csum & 0xff00;
+   c1 = *csum & 0x00ff;
+ 
+@@ -70,124 +69,8 @@
+   if (c0 == 0 || c1 == 0)
+     return 1;
+ 
+-  /*
+-   * Otherwise initialize to zero and calculate...
+-   */
+-  c0 = 0;
+-  c1 = 0;
+-
+-  while (len)
+-    {
+-      partial_len = MIN(len, 5803);
+-
+-      for (i = 0; i < partial_len; i++)
+-	{
+-	  c0 = c0 + *(p++);
+-	  c1 += c0;
+-	}
+-
+-      c0 = c0 % 255;
+-      c1 = c1 % 255;
+-
+-      len -= partial_len;
+-    }
+-
+-  if (c0 == 0 && c1 == 0)
+-    return 0;
+-
++  checksum = fletcher_checksum(buffer, len, (u_char *)csum - buffer);
++  if (checksum == *csum)
++      return 0;
+   return 1;
+ }
+-
+-/*
+- * Creates the checksum. *csum points to the position of the checksum in the 
+- * PDU. 
+- * Based on Annex C.4 of ISO/IEC 8473
+- */
+-#define FIXED_CODE
+-u_int16_t
+-iso_csum_create (u_char * buffer, int len, u_int16_t n)
+-{
+-
+-  u_int8_t *p;
+-  int x;
+-  int y;
+-  u_int32_t mul;
+-  u_int32_t c0;
+-  u_int32_t c1;
+-  u_int16_t checksum;
+-  u_int16_t *csum;
+-  int i, init_len, partial_len;
+-
+-  checksum = 0;
+-
+-  /*
+-   * Zero the csum in the packet.
+-   */
+-  csum = (u_int16_t *) (buffer + n);
+-  *(csum) = checksum;
+-
+-  p = buffer;
+-  c0 = 0;
+-  c1 = 0;
+-  init_len = len;
+-
+-  while (len != 0)
+-    {
+-      partial_len = MIN(len, 5803);
+-
+-      for (i = 0; i < partial_len; i++)
+-	{
+-	  c0 = c0 + *(p++);
+-	  c1 += c0;
+-	}
+-
+-      c0 = c0 % 255;
+-      c1 = c1 % 255;
+-
+-      len -= partial_len;
+-    }
+-
+-  mul = (init_len - n)*(c0);
+-
+-#ifdef FIXED_CODE
+-  x = mul - c0 - c1;
+-  y = c1 - mul - 1;
+-
+-  if (y > 0)
+-    y++;
+-  if (x < 0)
+-    x--;
+-
+-  x %= 255;
+-  y %= 255;
+-
+-  if (x == 0)
+-    x = 255;
+-  if (y == 0)
+-    y = 1;
+-
+-  checksum = (y << 8) | (x & 0xFF);
+-
+-#else
+-  x = mul - c0 - c1;
+-  x %= 255;
+-
+-  y = c1 - mul - 1;
+-  y %= 255;
+-
+-  if (x == 0)
+-    x = 255;
+-  if (y == 0)
+-    y = 255;
+-
+-  checksum = ((y << 8) | x);
+-#endif
+-
+-  /*
+-   * Now we write this to the packet
+-   */
+-  *(csum) = checksum;
+-
+-  /* return the checksum for user usage */
+-  return checksum;
+-}
+diff -ur quagga-0.99.8/isisd/iso_checksum.h quagga-unified-checksum/isisd/iso_checksum.h
+--- isisd/iso_checksum.h
++++ isisd/iso_checksum.h
+@@ -24,6 +24,5 @@
+ #define _ZEBRA_ISO_CSUM_H
+ 
+ int iso_csum_verify (u_char * buffer, int len, uint16_t * csum);
+-u_int16_t iso_csum_create (u_char * buffer, int len, u_int16_t n);
+ 
+ #endif /* _ZEBRA_ISO_CSUM_H */
+diff -ur quagga-0.99.8/lib/checksum.c quagga-unified-checksum/lib/checksum.c
+--- lib/checksum.c
++++ lib/checksum.c
+@@ -45,3 +45,82 @@
+ 	answer = ~sum;		/* ones-complement, then truncate to 16 bits */
+ 	return(answer);
+ }
++
++/* Fletcher Checksum -- Refer to RFC1008. */
++#define MODX                 4102   /* 5802 should be fine */
++
++/* To be consistent, offset is 0-based index, rather than the 1-based 
++   index required in the specification ISO 8473, Annex C.1 */
++u_int16_t
++fletcher_checksum(u_char * buffer, int len, u_int16_t offset)
++{
++  u_int8_t *p;
++  int x;
++  int y;
++  u_int32_t mul;
++  u_int32_t c0;
++  u_int32_t c1;
++  u_int16_t checksum;
++  u_int16_t *csum;
++  int i, init_len, partial_len;
++
++  checksum = 0;
++
++  /*
++   * Zero the csum in the packet.
++   */
++  csum = (u_int16_t *) (buffer + offset);
++  *(csum) = checksum;
++
++  p = buffer;
++  c0 = 0;
++  c1 = 0;
++  init_len = len;
++
++  while (len != 0)
++    {
++      partial_len = MIN(len, MODX);
++
++      for (i = 0; i < partial_len; i++)
++	{
++	  c0 = c0 + *(p++);
++	  c1 += c0;
++	}
++
++      c0 = c0 % 255;
++      c1 = c1 % 255;
++
++      len -= partial_len;
++    }
++
++  mul = (init_len - offset)*(c0);
++
++  x = mul - c0 - c1;
++  y = c1 - mul - 1;
++
++  if (y > 0)
++    y++;
++  if (x < 0)
++    x--;
++
++  x %= 255;
++  y %= 255;
++
++  if (x == 0)
++    x = 255;
++  if (y == 0)
++    y = 1;
++
++  /*
++   * Now we write this to the packet.
++   * We could skip this step too, since the checksum returned would
++   * be stored into the checksum field by the caller.
++   */
++  buffer[offset] = x;
++  buffer[offset + 1] = y;
++
++  /* Take care of the endian issue */
++  checksum = htons((x << 8) | (y & 0xFF));
++
++  return checksum;
++}
+diff -ur quagga-0.99.8/lib/checksum.h quagga-unified-checksum/lib/checksum.h
+--- lib/checksum.h
++++ lib/checksum.h
+@@ -1 +1,2 @@
+ extern int in_cksum(void *, int);
++extern u_int16_t fletcher_checksum(u_char * buffer, int len, u_int16_t offset);
+diff -ur quagga-0.99.8/ospfd/ospf_lsa.c quagga-unified-checksum/ospfd/ospf_lsa.c
+--- ospfd/ospf_lsa.c
++++ ospfd/ospf_lsa.c
+@@ -32,6 +32,7 @@
+ #include "thread.h"
+ #include "hash.h"
+ #include "sockunion.h"		/* for inet_aton() */
++#include "checksum.h"
+ 
+ #include "ospfd/ospfd.h"
+ #include "ospfd/ospf_interface.h"
+@@ -172,46 +173,22 @@
+ 
+ 
+ /* Fletcher Checksum -- Refer to RFC1008. */
+-#define MODX                 4102
+-#define LSA_CHECKSUM_OFFSET    15
+ 
++/* All the offsets are zero-based. The offsets in the RFC1008 are 
++   one-based. */
+ u_int16_t
+ ospf_lsa_checksum (struct lsa_header *lsa)
+ {
+-  u_char *sp, *ep, *p, *q;
+-  int c0 = 0, c1 = 0;
+-  int x, y;
+-  u_int16_t length;
+-
+-  lsa->checksum = 0;
+-  length = ntohs (lsa->length) - 2;
+-  sp = (u_char *) &lsa->options;
+-
+-  for (ep = sp + length; sp < ep; sp = q)
+-    {
+-      q = sp + MODX;
+-      if (q > ep)
+-        q = ep;
+-      for (p = sp; p < q; p++)
+-        {
+-          c0 += *p;
+-          c1 += c0;
+-        }
+-      c0 %= 255;
+-      c1 %= 255;
+-    }
++  u_char *buffer = (u_char *) &lsa->options;
++  int options_offset = buffer - (u_char *) &lsa->ls_age; /* should be 2 */
+ 
+-  x = (((int)length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255;
+-  if (x <= 0)
+-    x += 255;
+-  y = 510 - c0 - x;
+-  if (y > 255)
+-    y -= 255;
++  /* Skip the AGE field */
++  u_int16_t len = ntohs(lsa->length) - options_offset; 
+ 
+-  /* take care endian issue. */
+-  lsa->checksum = htons ((x << 8) + y);
++  /* Checksum offset starts from "options" field, not the beginning of the
++     lsa_header struct. The offset is 14, rather than 16. */
+ 
+-  return (lsa->checksum);
++  return fletcher_checksum(buffer, len, (u_char *) &lsa->checksum - buffer);
+ }
+ 
+ 
+diff -ur quagga-0.99.8/solaris/quagga.init.in quagga-unified-checksum/solaris/quagga.init.in
+--- solaris/quagga.init.in
++++ solaris/quagga.init.in
+@@ -134,7 +134,7 @@
+ case "${DAEMON}" in
+ 	bgpd)
+ 	;;
+-	zebra | ospfd | ospf6d | ripd | ripngd )
++	zebra | ospfd | ospf6d | ripd | ripngd | isisd)
+ 		quagga_is_globalzone || exit $SMF_EXIT_OK
+ 	;;
+ 	*)
+@@ -168,7 +168,7 @@
+ 	eval exec $DAEMON_PATH/$DAEMON $DAEMON_ARGS --pid_file ${PIDFILE} &
+ }
+ 
+-stop () {
++daemonstop () {
+ 	if [ -f "${PIDFILE}" ]; then
+ 		/usr/bin/kill -TERM `/usr/bin/cat "${PIDFILE}"`
+ 	fi
+@@ -179,7 +179,7 @@
+ 	start
+ 	;;
+ 'stop')
+-	stop
++	daemonstop
+ 	;;
+ 
+ *)
+diff -ur quagga-0.99.8/solaris/quagga.xml.in quagga-unified-checksum/solaris/quagga.xml.in
+--- solaris/quagga.xml.in
++++ solaris/quagga.xml.in
+@@ -187,7 +187,7 @@
+ 		<service_fmri value='svc:/network/routing-setup' />
+ 	</dependency>
+ 	
+-	<!-- ensure that restart of zebra is propogated to daemon -->
++	<!-- ensure that restart of zebra is propagated to daemon -->
+ 	<dependency
+ 		name='zebra'
+ 		grouping='optional_all'
+@@ -818,6 +818,148 @@
+ 			<manpage title='bgpd' section='1M'
+ 				manpath='@mandir@' />
+ 			<doc_link name='quagga.net' 
++				uri='http://www.quagga.net/' />
++		</documentation>
++	</template>
++	</instance>
++	<stability value='Unstable' />
++</service>
++
++<service
++	name='network/routing/isis'
++	type='service'
++	version='1'>
++
++	<instance name='quagga' enabled='false'>
++
++	<dependency name='fs'
++		grouping='require_all'
++		restart_on='none'
++		type='service'>
++		<service_fmri
++			value='svc:/system/filesystem/usr:default' />
++	</dependency>
++
++	<dependency
++		name='ipv6-forwarding'
++		grouping='optional_all'
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/ipv6-forwarding' />
++	</dependency>
++	
++	<dependency
++		name='ipv4-forwarding'
++		grouping='optional_all'
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/ipv4-forwarding' />
++	</dependency>
++
++	<!-- do not not run unless routing-setup has run -->
++	<dependency
++		name='network_routing_setup'
++		grouping='require_all'
++		restart_on='refresh'
++		type='service'>
++		<service_fmri value='svc:/network/routing-setup' />
++	</dependency>
++
++	<!-- ensure that restart of zebra is propogated to daemon -->
++	<dependency
++		name='zebra'
++		grouping='optional_all'
++		restart_on='restart'
++		type='service'>
++		<service_fmri value='svc:/network/routing/zebra:quagga' />
++	</dependency>
++
++	<exec_method
++		type='method'
++		name='start'
++		exec='/lib/svc/method/quagga isisd'
++		timeout_seconds='60'>
++		<method_context>
++		  <method_credential
++		   user='root' group='root'/>
++ 		</method_context>
++	</exec_method>
++
++	<!-- isisd can take a long time to shutdown, due to graceful 
++	     shutdown 
++	 -->
++	<exec_method
++		type='method'
++		name='stop'
++		exec=':kill'
++		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>
++
++	<!-- Properties in this group are used by routeadm (1M) -->
++	<property_group name='routeadm' type='application'>
++		<stability value='Unstable' />
++		<!-- Identifies service as a routing service -->
++		<propval name='daemon' type='astring'
++			value='@sbindir@/isisd' />
++		<propval name='value_authorization' type='astring'
++			value='solaris.smf.value.routing' />
++		<property name='protocol' type='astring'>
++			<astring_list>
++				<value_node value='ipv4'/>
++				<value_node value='ipv6'/>
++			</astring_list>
++		</property>
++	</property_group>
++	
++	<!-- Properties in this group are modifiable via routeadm (1M) -->
++	<property_group name='routing' type='application'>
++		<propval name='value_authorization' type='astring' 
++		         value='solaris.smf.value.routing' />
++
++		<!-- Options common to Quagga daemons -->
++		<!-- The config file to use, if not the default -->
++		<propval name='config_file' type='astring' value=''/>
++		<!-- The vty_port to listen on if not the default.
++		     0 to disable --> 
++		<propval name='vty_port' type='integer' value='0' />
++		<!-- The address to bind the VTY interface to, if not any. -->
++		<propval name='vty_address' type='astring' value='' />
++		<!-- The user to switch to after startup, if not the default -->
++		<propval name='user' type='astring' value='' />
++		<!-- The group to switch to, if not the default.
++		     If user is specified, this defaults to a group with
++		     same name as user -->
++		<propval name='group' type='astring' value='' />
++		<!-- The pidfile to use, if not the default of
++		     @quagga_statedir@ -->
++		<propval name='pid_file' type='astring' value='' />
++	</property_group>
++
++	<property_group name='general' type='framework'>
++		<!-- to start stop routing services -->
++		<propval name='action_authorization' type='astring'
++			 value='solaris.smf.manage.routing' />
++		<propval name='value_authorization' type='astring'
++			 value='solaris.smf.manage.routing' />
++	</property_group>
++
++	<template>
++		<common_name>
++			<loctext xml:lang='C'>
++			Quagga: isisd, IS-IS routing protocol daemon.
++			</loctext>
++		</common_name>
++		<documentation>
++			<manpage title='isisd' section='1M'
++				manpath='@mandir@' />
++			<doc_link name='quagga.net' 
+ 				uri='http://www.quagga.net/' />
+ 		</documentation>
+ 	</template>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/70-isisd-trill.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,8495 @@
+diff --git configure.ac configure.ac
+index 78198cb..12e9729 100755
+--- configure.ac
++++ configure.ac
+@@ -197,6 +197,10 @@ AC_ARG_ENABLE(watchquagga,
+ [  --disable-watchquagga   do not build watchquagga])
+ AC_ARG_ENABLE(isisd,
+ [  --enable-isisd          build isisd])
++AC_ARG_ENABLE(trill,
++[  --enable-trill          include TRILL support])
++AC_ARG_ENABLE(solaris,
++[  --enable-solaris          build solaris])
+ AC_ARG_ENABLE(bgp-announce,
+ [  --disable-bgp-announce, turn off BGP route announcement])
+ AC_ARG_ENABLE(netlink,
+@@ -311,6 +315,30 @@ AC_SUBST(ISIS_TOPOLOGY_INCLUDES)
+ AC_SUBST(ISIS_TOPOLOGY_DIR)
+ AC_SUBST(ISIS_TOPOLOGY_LIB)
+ 
++if test "${enable_trill}" = "yes"; then
++  AC_CHECK_HEADER(net/trill.h)
++  AC_CHECK_LIB(dladm, dladm_valid_bridgename, libdladm=yes)
++  AC_MSG_CHECKING(TRILL IS-IS support)
++  if test $ac_cv_header_net_trill_h = no || \
++    test $ac_cv_lib_dladm_dladm_valid_bridgename = no; then
++    AC_MSG_RESULT(none)
++    AC_MSG_WARN([*** TRILL IS-IS support will not be built ***])
++    enable_trill=no
++  else
++    AC_MSG_RESULT(yes)
++    AC_DEFINE(HAVE_TRILL,,Enable TRILL support)
++  fi
++fi
++if test "${enable_trill}" = "yes"; then
++  ISIS_TARGETS="isisd trilld"
++  ISIS_LIBS=-ldladm
++else
++  ISIS_TARGETS="isisd"
++  ISIS_LIBS=
++fi
++AC_SUBST(ISIS_TARGETS)
++AC_SUBST(ISIS_LIBS)
++
+ if test "${enable_user}" = "yes" || test x"${enable_user}" = x""; then
+   enable_user="quagga"
+ elif test "${enable_user}" = "no"; then
+@@ -753,28 +781,31 @@ AC_SUBST(OTHER_METHOD)
+ dnl --------------------------
+ dnl Determine IS-IS I/O method
+ dnl --------------------------
+-AC_CHECK_HEADER(net/bpf.h)
+-AC_CHECK_HEADER(sys/dlpi.h)
+-AC_MSG_CHECKING(zebra IS-IS I/O method)
+-if test x"$opsys" = x"gnu-linux"; then
+-  AC_MSG_RESULT(pfpacket)
+-  ISIS_METHOD=isis_pfpacket.o
+-elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
+-  AC_MSG_RESULT(DLPI)
+-  ISIS_METHOD="isis_dlpi.o"
+-else
+-  if test $ac_cv_header_net_bpf_h = no; then
+-    if test $ac_cv_header_sys_dlpi_h = no; then
+-      AC_MSG_RESULT(none)
+-      AC_MSG_WARN([*** IS-IS support will not be built ***])
+-      ISISD=""
+-    else
+-      AC_MSG_RESULT(DLPI)
+-    fi
++if test "${enable_isisd}" = "yes"; then
++  ISIS_METHOD=
++  AC_CHECK_HEADER(net/bpf.h)
++  AC_CHECK_HEADER(sys/dlpi.h)
++  AC_MSG_CHECKING(zebra IS-IS I/O method)
++  if test x"$opsys" = x"gnu-linux"; then
++    AC_MSG_RESULT(pfpacket)
++    ISIS_METHOD=isis_pfpacket.o
++  elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then
++    AC_MSG_RESULT(DLPI)
+     ISIS_METHOD="isis_dlpi.o"
+   else
+-    AC_MSG_RESULT(BPF)
+-    ISIS_METHOD="isis_bpf.o"
++    if test $ac_cv_header_net_bpf_h = no; then
++      if test $ac_cv_header_sys_dlpi_h = no; then
++	AC_MSG_RESULT(none)
++	AC_MSG_WARN([*** IS-IS support will not be built ***])
++	ISISD=""
++      else
++	AC_MSG_RESULT(DLPI)
++      fi
++      ISIS_METHOD="isis_dlpi.o"
++    else
++      AC_MSG_RESULT(BPF)
++      ISIS_METHOD="isis_bpf.o"
++    fi
+   fi
+ fi
+ AC_SUBST(ISIS_METHOD)
+diff --git isisd/Makefile.am isisd/Makefile.am
+index 859facd..9adcc05 100644
+--- isisd/Makefile.am
++++ isisd/Makefile.am
+@@ -4,10 +4,11 @@ INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib \
+ 	   @ISIS_TOPOLOGY_INCLUDES@
+ DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+ INSTALL_SDATA=@INSTALL@ -m 600
+-LIBS = @LIBS@ 
++LIBS = @LIBS@ @ISIS_LIBS@
+ noinst_LIBRARIES = libisis.a
+-sbin_PROGRAMS = isisd 
++sbin_PROGRAMS = @ISIS_TARGETS@
+ SUBDIRS = topology
++EXTRA_PROGRAMS = isisd trilld
+ 
+ isis_method = @ISIS_METHOD@
+ 
+@@ -23,16 +24,24 @@ noinst_HEADERS = \
+ 	isis_lsp.h dict.h isis_circuit.h isis_misc.h isis_network.h \
+ 	isis_zebra.h isis_dr.h isis_flags.h isis_dynhn.h isis_common.h \
+ 	iso_checksum.h isis_csm.h isis_events.h isis_spf.h isis_route.h \
++	isis_trill.h \
+ 	include-netbsd/clnp.h include-netbsd/esis.h include-netbsd/iso.h
+ 
+ isisd_SOURCES = \
+-	isis_main.c $(libisis_a_SOURCES)
++	isis_main.c $(libisis_a_SOURCES) isis_trilldummy.c
+ 
+-isisd_LDADD = $(isis_method) @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@
++isisd_LDADD = $(isis_method) @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@ @LIBM@
+ 
+ isisd_DEPENDENCIES = $(isis_method)
+ 
+-EXTRA_DIST = isis_bpf.c isis_dlpi.c isis_pfpacket.c
++trilld_SOURCES = \
++	isis_main.c $(libisis_a_SOURCES) isis_trill.c isis_trillio.c \
++	isis_trillvlans.c isis_trillbpdu.c
++
++trilld_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@ @LIBM@
++
++EXTRA_DIST = isis_bpf.c isis_dlpi.c isis_pfpacket.c isis_trill.c \
++	isis_trillio.c isis_trillvlans.c isis_trillbpdu.c
+ 
+ examplesdir = $(exampledir)
+ dist_examples_DATA = isisd.conf.sample
+diff --git isisd/bool.h isisd/bool.h
+new file mode 100644
+index 0000000..e713d65
+--- /dev/null
++++ isisd/bool.h
+@@ -0,0 +1,25 @@
++/*
++ * IS-IS Rout(e)ing protocol               - bool.h
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#ifndef _ZEBRA_ISIS_BOOL_H
++#define _ZEBRA_ISIS_BOOL_H
++
++#define FALSE 0
++#define TRUE 1
++
++#endif
+diff --git isisd/dict.h isisd/dict.h
+index 9395d1c..0a5382c 100644
+--- isisd/dict.h
++++ isisd/dict.h
+@@ -124,6 +124,11 @@ extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
+ extern void dict_load_end(dict_load_t *);
+ extern void dict_merge(dict_t *, dict_t *);
+ 
++#define ALL_DICT_NODES_RO(D,dnode,data) \
++  (dnode) = dict_first((D)); \
++  (dnode) != NULL && ((data) = dnode_get((dnode)), 1); \
++  (dnode) = dict_next((D),(dnode))
++
+ #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ #ifdef KAZLIB_SIDEEFFECT_DEBUG
+ #define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
+diff --git isisd/isis_adjacency.c isisd/isis_adjacency.c
+index aab8d1a..4b2159b 100644
+--- isisd/isis_adjacency.c
++++ isisd/isis_adjacency.c
+@@ -43,6 +43,9 @@
+ #include "isisd/isis_dr.h"
+ #include "isisd/isis_dynhn.h"
+ #include "isisd/isis_pdu.h"
++#include "isisd/isis_tlv.h"
++#include "isisd/isis_lsp.h"
++#include "isisd/isis_vlans.h"
+ 
+ extern struct isis *isis;
+ 
+@@ -53,6 +56,10 @@ adj_alloc (u_char * id)
+ 
+   adj = XCALLOC (MTYPE_ISIS_ADJACENCY, sizeof (struct isis_adjacency));
+   memcpy (adj->sysid, id, ISIS_SYS_ID_LEN);
++  adj->lsps = list_new();
++#ifdef HAVE_TRILL
++  adj->vlans = XCALLOC (MTYPE_ISIS_TRILL_ADJVLANS, sizeof (struct trill_adj_vlans));
++#endif
+ 
+   return adj;
+ }
+@@ -127,6 +134,9 @@ isis_adj_lookup_snpa (u_char * ssnpa, struct list *adjdb)
+ void
+ isis_delete_adj (struct isis_adjacency *adj, struct list *adjdb)
+ {
++  struct listnode *node;
++  struct isis_lsp *lsp;
++
+   if (!adj)
+     return;
+   /* When we recieve a NULL list, we will know its p2p. */
+@@ -141,7 +151,18 @@ isis_delete_adj (struct isis_adjacency *adj, struct list *adjdb)
+   if (adj->ipv6_addrs)
+     list_delete (adj->ipv6_addrs);
+ #endif
+-  
++
++  /* clear adj LSPs list (tracks LSPs recvd from the adj) */
++  if (adj->lsps)
++    {
++      for (ALL_LIST_ELEMENTS_RO (adj->lsps, node, lsp))
++        lsp->adj = NULL;
++      list_delete (adj->lsps);
++    }
++
++#ifdef HAVE_VLAN
++  XFREE (MTYPE_ISIS_TRILL_ADJVLANS, adj->vlans);
++#endif
+   XFREE (MTYPE_ISIS_ADJACENCY, adj);
+   return;
+ }
+@@ -179,6 +200,10 @@ isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state state,
+       list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]);
+       isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1],
+ 				 circuit->u.bc.lan_neighs[level - 1]);
++
++      /* On adjacency state change send new pseudo LSP if we are the DR */
++      if (circuit->u.bc.is_dr[level - 1])
++        lsp_pseudo_regenerate (circuit, level);
+     }
+   else if (state == ISIS_ADJ_UP)
+     {				/* p2p interface */
+@@ -302,6 +327,11 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail)
+   struct isis_dynhn *dyn;
+   int level;
+   struct listnode *node;
++#ifdef HAVE_TRILL
++  int vlan_count = 0;
++  int vlan_set;
++  int vlan;
++#endif
+ 
+   dyn = dynhn_find_by_id (adj->sysid);
+   if (dyn)
+@@ -388,6 +418,38 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail)
+ 	    }
+ 	}
+ #endif /* HAVE_IPV6 */
++
++#ifdef HAVE_TRILL
++      vty_out (vty, "    Designated VLAN: %d", adj->vlans->designated);
++      vty_out (vty, "%s    VLAN Forwarder: ", VTY_NEWLINE);
++      EACH_VLAN_SET(adj->vlans->forwarder, vlan, vlan_set)
++        {
++	   vlan_count++;
++	   if (vlan_count % 8 == 0)
++             vty_out(vty, "%s                     ", VTY_NEWLINE);
++           vty_out (vty, "%d ", vlan); 
++        }
++      vty_out (vty, "%s    Enabled VLANs: ", VTY_NEWLINE);
++      vlan_count = 0;
++      EACH_VLAN_SET(adj->vlans->enabled, vlan, vlan_set)
++        {
++	   vlan_count++;
++	   if (vlan_count % 8 == 0)
++             vty_out(vty, "%s                     ", VTY_NEWLINE);
++           vty_out (vty, "%d ", vlan); 
++        }
++      vty_out (vty, "%s    Rx VLANs: ", VTY_NEWLINE);
++      vlan_count = 0;
++      EACH_VLAN_SET(adj->vlans->seen, vlan, vlan_set)
++        {
++	   vlan_count++;
++	   if (vlan_count % 8 == 0)
++             vty_out(vty, "%s                     ", VTY_NEWLINE);
++           vty_out (vty, "%d ", vlan); 
++        }
++      vty_out (vty, "%s", VTY_NEWLINE);
++#endif /* HAVE_TRILL */
++
+       vty_out (vty, "%s", VTY_NEWLINE);
+     }
+   return;
+diff --git isisd/isis_adjacency.h isisd/isis_adjacency.h
+index 99a8bb2..b966230 100644
+--- isisd/isis_adjacency.h
++++ isisd/isis_adjacency.h
+@@ -96,6 +96,11 @@ struct isis_adjacency
+   int flaps;			/* number of adjacency flaps  */
+   struct thread *t_expire;	/* expire after hold_time  */
+   struct isis_circuit *circuit;	/* back pointer */
++  struct list *lsps;		/* LSPs marked with this adjacency */
++
++#ifdef HAVE_TRILL
++  struct trill_adj_vlans *vlans;
++#endif
+ };
+ 
+ struct isis_adjacency *isis_adj_lookup (u_char * sysid, struct list *adjdb);
+diff --git isisd/isis_circuit.c isisd/isis_circuit.c
+index af24988..8de3622 100644
+--- isisd/isis_circuit.c
++++ isisd/isis_circuit.c
+@@ -58,6 +58,11 @@
+ #include "isisd/isis_csm.h"
+ #include "isisd/isis_events.h"
+ 
++#ifdef HAVE_TRILL
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
++#endif
++
+ extern struct thread_master *master;
+ extern struct isis *isis;
+ 
+@@ -87,6 +92,11 @@ isis_circuit_new ()
+ 	  circuit->metrics[i].metric_delay = METRICS_UNSUPPORTED;
+ 	  circuit->te_metric[i] = DEFAULT_CIRCUIT_METRICS;
+ 	}
++
++#ifdef HAVE_TRILL
++      circuit->vlans = XCALLOC (MTYPE_ISIS_TRILL_VLANS,
++          sizeof(struct trill_circuit_vlans));
++#endif
+     }
+   else
+     {
+@@ -126,6 +136,13 @@ isis_circuit_configure (struct isis_circuit *circuit, struct isis_area *area)
+     }
+   circuit->lsp_interval = LSP_INTERVAL;
+ 
++#ifdef HAVE_TRILL
++  circuit->vlans->pvid = DFLT_VLAN;
++  circuit->vlans->designated = DFLT_VLAN;
++  circuit->vlans->our_designated = DFLT_VLAN;
++  SET_VLAN(circuit->vlans->enabled, DFLT_VLAN);
++#endif
++
+   /*
+    * Add the circuit into area
+    */
+@@ -216,6 +233,26 @@ isis_circuit_del (struct isis_circuit *circuit)
+     list_delete (circuit->ipv6_non_link);
+ #endif /* HAVE_IPV6 */
+ 
++#ifdef HAVE_TRILL
++  if (circuit->vlans != NULL)
++    {
++      struct trill_circuit_vlans *cvlans = circuit->vlans;
++
++      if (cvlans->appvlanfwders != NULL)
++	list_delete (cvlans->appvlanfwders);
++      if (cvlans->enabled_vlans != NULL)
++	list_delete (cvlans->enabled_vlans);
++      if (cvlans->inhibit_vlans != NULL)
++	list_delete (cvlans->inhibit_vlans);
++      if (cvlans->inhibit_thread != NULL)
++	thread_cancel (cvlans->inhibit_thread);
++      XFREE (MTYPE_ISIS_TRILL_VLANS, cvlans);
++    }
++
++  if (circuit->tc_thread != NULL)
++    thread_cancel (circuit->tc_thread);
++#endif
++
+   /* and lastly the circuit itself */
+   XFREE (MTYPE_ISIS_CIRCUIT, circuit);
+ 
+@@ -374,7 +411,7 @@ isis_circuit_if_add (struct isis_circuit *circuit, struct interface *ifp)
+   circuit->interface = ifp;
+   ifp->info = circuit;
+ 
+-  circuit->circuit_id = ifp->ifindex % 255;	/* FIXME: Why not ? */
++  circuit->circuit_id = (ifp->ifindex % 255) + 1; /* FIXME: Why not ? */
+ 
+   /*  isis_circuit_update_addrs (circuit, ifp); */
+ 
+@@ -436,7 +473,7 @@ isis_circuit_update_params (struct isis_circuit *circuit,
+     {
+       zlog_warn ("changing circuit_id %d->%d", circuit->circuit_id,
+ 		 ifp->ifindex);
+-      circuit->circuit_id = ifp->ifindex % 255;
++      circuit->circuit_id = (ifp->ifindex % 255) + 1;
+     }
+ 
+   /* FIXME: Why is this needed? shouldn't we compare to the area's mtu */
+@@ -500,9 +537,10 @@ isis_circuit_if_del (struct isis_circuit *circuit)
+   return;
+ }
+ 
+-void
++int
+ isis_circuit_up (struct isis_circuit *circuit)
+ {
++  int retv;
+ 
+   if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+     {
+@@ -565,43 +603,57 @@ isis_circuit_up (struct isis_circuit *circuit)
+ 		       isis_jitter (circuit->psnp_interval[1], PSNP_JITTER));
+     }
+ 
+-  /* initialize the circuit streams */
++  /* unified init for circuits; ignore warnings below this level */
++  retv = isis_sock_init (circuit);
++  if (retv == ISIS_ERROR)
++    {
++      isis_circuit_down (circuit);
++      return retv;
++    }
++
++  /* initialize the circuit streams after opening connection */
+   if (circuit->rcv_stream == NULL)
+     circuit->rcv_stream = stream_new (ISO_MTU (circuit));
+ 
+   if (circuit->snd_stream == NULL)
+     circuit->snd_stream = stream_new (ISO_MTU (circuit));
+ 
+-  /* unified init for circuits */
+-  isis_sock_init (circuit);
+-
+-#ifdef GNU_LINUX
++#if defined(GNU_LINUX) || defined(SUNOS_5)
+   THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit,
+ 		  circuit->fd);
+ #else
+   THREAD_TIMER_ON (master, circuit->t_read, isis_receive, circuit,
+ 		   circuit->fd);
+ #endif
+-  return;
++  return ISIS_OK;
+ }
+ 
+ void
+ isis_circuit_down (struct isis_circuit *circuit)
+ {
+   /* Cancel all active threads -- FIXME: wrong place */
+-  /* HT: Read thread if GNU_LINUX, TIMER thread otherwise. */
++  /* HT: Read thread if GNU_LINUX or SUNOS_5, TIMER thread otherwise. */
+   THREAD_OFF (circuit->t_read);
++  THREAD_TIMER_OFF (circuit->t_send_csnp[0]);
++  THREAD_TIMER_OFF (circuit->t_send_csnp[1]);
++  THREAD_TIMER_OFF (circuit->t_send_psnp[0]);
++  THREAD_TIMER_OFF (circuit->t_send_psnp[1]);
+   if (circuit->circ_type == CIRCUIT_T_BROADCAST)
+     {
+       THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[0]);
+       THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[1]);
+       THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[0]);
+       THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[1]);
++      THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[0]);
++      THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[1]);
+     }
+   else if (circuit->circ_type == CIRCUIT_T_P2P)
+     {
+       THREAD_TIMER_OFF (circuit->u.p2p.t_send_p2p_hello);
+     }
++#ifdef HAVE_TRILL
++  THREAD_TIMER_OFF (circuit->tc_thread);
++#endif
+   /* close the socket */
+   close (circuit->fd);
+ 
+diff --git isisd/isis_circuit.h isisd/isis_circuit.h
+index a7e719f..fd26a27 100644
+--- isisd/isis_circuit.h
++++ isisd/isis_circuit.h
+@@ -137,6 +137,14 @@ struct isis_circuit
+   u_int32_t ctrl_pdus_txed;	/* controlPDUsSent */
+   u_int32_t desig_changes[2];	/* lanLxDesignatedIntermediateSystemChanges */
+   u_int32_t rej_adjacencies;	/* rejectedAdjacencies */
++
++#ifdef HAVE_TRILL
++  struct trill_circuit_vlans *vlans;	/* TRILL VLANs */
++  u_int8_t root_bridge[8];		/* STP Root Bridge */
++  time_t root_expire;			/* time when root expires */
++  int tc_count;
++  struct thread *tc_thread;
++#endif
+ };
+ 
+ void isis_circuit_init (void);
+@@ -147,7 +155,7 @@ struct isis_circuit *circuit_scan_by_ifp (struct interface *ifp);
+ void isis_circuit_del (struct isis_circuit *circuit);
+ void isis_circuit_configure (struct isis_circuit *circuit,
+ 			     struct isis_area *area);
+-void isis_circuit_up (struct isis_circuit *circuit);
++int isis_circuit_up (struct isis_circuit *circuit);
+ void isis_circuit_deconfigure (struct isis_circuit *circuit,
+ 			       struct isis_area *area);
+ 
+diff --git isisd/isis_common.h isisd/isis_common.h
+index 2633855..29baf1e 100644
+--- isisd/isis_common.h
++++ isisd/isis_common.h
+@@ -21,6 +21,9 @@
+  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  */
+ 
++#ifndef ISIS_COMMON_H
++#define ISIS_COMMON_H
++
+ /*
+  * Area Address
+  */
+@@ -72,3 +75,5 @@ struct flags
+   int maxindex;
+   struct list *free_idcs;
+ };
++
++#endif
+diff --git isisd/isis_constants.h isisd/isis_constants.h
+index 1b75ba6..50a526c 100644
+--- isisd/isis_constants.h
++++ isisd/isis_constants.h
+@@ -102,6 +102,10 @@
+ #define IS_LEVEL_2       2
+ #define IS_LEVEL_1_AND_2 3
+ 
++#ifdef HAVE_TRILL
++#define TRILL_ISIS_LEVEL  IS_LEVEL_1		/* Use ISIS level 1 for TRILL */
++#endif
++
+ #define SNPA_ADDRSTRLEN 18
+ #define ISIS_SYS_ID_LEN  6
+ #define SYSID_STRLEN    24
+diff --git isisd/isis_csm.c isisd/isis_csm.c
+index 80d0c90..c5bb42d 100644
+--- isisd/isis_csm.c
++++ isisd/isis_csm.c
+@@ -110,7 +110,11 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
+ 	{
+ 	case ISIS_ENABLE:
+ 	  isis_circuit_configure (circuit, (struct isis_area *) arg);
+-	  isis_circuit_up (circuit);
++	  if (isis_circuit_up (circuit) != ISIS_OK)
++	    {
++	      isis_circuit_deconfigure (circuit, (struct isis_area *) arg);
++	      break;
++	    }
+ 	  circuit->state = C_STATE_UP;
+ 	  isis_event_circuit_state_change (circuit, 1);
+ 	  listnode_delete (isis->init_circ_list, circuit);
+@@ -137,7 +141,11 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
+ 	  break;
+ 	case IF_UP_FROM_Z:
+ 	  isis_circuit_if_add (circuit, (struct interface *) arg);
+-	  isis_circuit_up (circuit);
++	  if (isis_circuit_up (circuit) != ISIS_OK)
++	    {
++	      isis_circuit_if_del (circuit);
++	      break;
++	    }
+ 	  circuit->state = C_STATE_UP;
+ 	  isis_event_circuit_state_change (circuit, 1);
+ 	  break;
+@@ -161,12 +169,14 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg)
+ 	  zlog_warn ("circuit already connected");
+ 	  break;
+ 	case ISIS_DISABLE:
++	  isis_circuit_down (circuit);
+ 	  isis_circuit_deconfigure (circuit, (struct isis_area *) arg);
+ 	  listnode_add (isis->init_circ_list, circuit);
+ 	  circuit->state = C_STATE_INIT;
+ 	  isis_event_circuit_state_change (circuit, 0);
+ 	  break;
+ 	case IF_DOWN_FROM_Z:
++	  isis_circuit_down (circuit);
+ 	  isis_circuit_if_del (circuit);
+ 	  circuit->state = C_STATE_CONF;
+ 	  isis_event_circuit_state_change (circuit, 0);
+diff --git isisd/isis_dlpi.c isisd/isis_dlpi.c
+index 07ab547..5837201 100644
+--- isisd/isis_dlpi.c
++++ isisd/isis_dlpi.c
+@@ -21,6 +21,8 @@
+  */
+ 
+ #include <zebra.h>
++#include <vty.h>
++
+ #include <net/if.h>
+ #include <netinet/if_ether.h>
+ #include <sys/types.h>
+@@ -301,7 +303,7 @@ dlpiaddr (int fd, u_char *addr)
+ static int
+ open_dlpi_dev (struct isis_circuit *circuit)
+ {
+-  int fd, unit, retval;
++  int fd = -1, unit, retval;
+   char devpath[MAXPATHLEN];
+   dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl;
+   ssize_t acklen;
+@@ -313,20 +315,21 @@ open_dlpi_dev (struct isis_circuit *circuit)
+ 	circuit->interface->name);
+       return ISIS_WARNING;
+     }
+-  
++
++  /* Try first as Style 1 */
+   /* Try the vanity node first, if permitted */
+   if (getenv("DLPI_DEVONLY") == NULL)
+     {
+-      (void) snprintf (devpath, sizeof(devpath), "/dev/net/%s",
+-                      circuit->interface->name);
+-      fd = dlpiopen (devpath, &acklen);
++      (void) snprintf(devpath, sizeof(devpath), "/dev/net/%s",
++        circuit->interface->name);
++      fd = dlpiopen(devpath, &acklen);
+     }
+-  
++
+   /* Now try as an ordinary Style 1 node */
+   if (fd == -1)
+     {
+-      (void) snprintf (devpath, sizeof (devpath), "/dev/%s",
+-                      circuit->interface->name);
++      (void) snprintf(devpath, sizeof (devpath), "/dev/%s",
++        circuit->interface->name);
+       unit = -1;
+       fd = dlpiopen (devpath, &acklen);
+     }
+@@ -402,8 +405,8 @@ open_dlpi_dev (struct isis_circuit *circuit)
+     case DL_100BT:
+       break;
+     default:
+-      zlog_warn ("%s: unexpected mac type on %s: %d", __func__,
+-	circuit->interface->name, dia->dl_mac_type);
++      zlog_warn ("%s: unexpected mac type on %s: %ld", __func__,
++	circuit->interface->name, (u_long)dia->dl_mac_type);
+       close (fd);
+       return ISIS_WARNING;
+     }
+@@ -471,9 +474,9 @@ open_dlpi_dev (struct isis_circuit *circuit)
+       sioc.ic_timout = 5;
+       sioc.ic_len = sizeof (struct packetfilt);
+       sioc.ic_dp = (char *)&pfil;
+-      if (ioctl (fd, I_STR, &sioc) == -1)
++      if (ioctl (fd, I_STR, &sioc) == -1) 
+          zlog_warn("%s: could not perform PF_IOCSETF on %s",
+-           __func__, circuit->interface->name);
++   	    __func__, circuit->interface->name);
+     }
+ 
+   circuit->fd = fd;
+diff --git isisd/isis_dr.c isisd/isis_dr.c
+index 8d306c8..a481142 100644
+--- isisd/isis_dr.c
++++ isisd/isis_dr.c
+@@ -207,17 +207,16 @@ isis_dr_elect (struct isis_circuit *circuit, int level)
+       || (adj_dr->prio[level - 1] == own_prio
+ 	  && memcmp (adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0))
+     {
+-      if (!circuit->u.bc.is_dr[level - 1])
+-	{
+-	  /*
+-	   * We are the DR
+-	   */
++      adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
++      adj_dr->dis_record[level - 1].last_dis_change = time (NULL);
+ 
+-	  /* rotate the history log */
+-	  for (ALL_LIST_ELEMENTS_RO (list, node, adj))
+-            isis_check_dr_change (adj, level);
++      /* rotate the history log */
++      for (ALL_LIST_ELEMENTS_RO (list, node, adj))
++        isis_check_dr_change (adj, level);
+ 
+-	  /* commence */
++      if (!circuit->u.bc.is_dr[level - 1])
++	{
++	  /* We are the DR, commence DR */
+ 	  list_delete (list);
+ 	  return isis_dr_commence (circuit, level);
+ 	}
+diff --git isisd/isis_flags.h isisd/isis_flags.h
+index 13dd9e1..4346eb7 100644
+--- isisd/isis_flags.h
++++ isisd/isis_flags.h
+@@ -27,6 +27,7 @@
+ /* The grand plan is to support 1024 circuits so we have 32*32 bit flags
+  * the support will be achived using the newest drafts */
+ #define ISIS_MAX_CIRCUITS 32 /* = 1024 */	/*FIXME:defined in lsp.h as well */
++#define ISIS_MAX_CIRCUITS_COUNT 32 * ISIS_MAX_CIRCUITS  /* total count of max circuits */
+ 
+ void flags_initialize (struct flags *flags);
+ struct flags *new_flags (int size);
+diff --git isisd/isis_lsp.c isisd/isis_lsp.c
+index 48e3147..53430a7 100644
+--- isisd/isis_lsp.c
++++ isisd/isis_lsp.c
+@@ -38,17 +38,22 @@
+ #include "isisd/dict.h"
+ #include "isisd/isis_constants.h"
+ #include "isisd/isis_common.h"
++#include "isisd/isis_flags.h"
+ #include "isisd/isis_circuit.h"
+-#include "isisd/isisd.h"
+ #include "isisd/isis_tlv.h"
+ #include "isisd/isis_lsp.h"
++#ifdef HAVE_TRILL
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
++#endif
++#include "isisd/isisd.h"
+ #include "isisd/isis_pdu.h"
+ #include "isisd/isis_dynhn.h"
+ #include "isisd/isis_misc.h"
+-#include "isisd/isis_flags.h"
+ #include "isisd/isis_csm.h"
+ #include "isisd/isis_adjacency.h"
+ #include "isisd/isis_spf.h"
++#include "isisd/bool.h"
+ 
+ #ifdef TOPOLOGY_GENERATE
+ #include "spgrid.h"
+@@ -138,18 +143,34 @@ lsp_clear_data (struct isis_lsp *lsp)
+   if (lsp->tlv_data.ipv6_reachs)
+     list_delete (lsp->tlv_data.ipv6_reachs);
+ #endif /* HAVE_IPV6 */
++  if (lsp->tlv_data.router_capabilities)
++    list_delete (lsp->tlv_data.router_capabilities);
+ 
+   memset (&lsp->tlv_data, 0, sizeof (struct tlvs));
+ 
+   return;
+ }
+ 
++/*
++ * clearnick is set by callers to indicate it is
++ * also safe to clear any nickname that was learnt from
++ * the LSP. LSP purge case is safe but LSP destroyed before
++ * replaced by a new LSP from the other RBridge is not. 
++ */
+ static void
+-lsp_destroy (struct isis_lsp *lsp)
++lsp_destroy (struct isis_lsp *lsp, int clearnick)
+ {
+   if (!lsp)
+     return;
+ 
++#ifdef HAVE_TRILL
++  if (clearnick)
++    trill_nick_destroy(lsp);
++#endif
++
++  if (lsp->adj != NULL && lsp->adj->lsps != NULL)
++    listnode_delete(lsp->adj->lsps, lsp);
++
+   lsp_clear_data (lsp);
+ 
+   if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0 && lsp->lspu.frags)
+@@ -173,7 +194,7 @@ lsp_db_destroy (dict_t * lspdb)
+     {
+       next = dict_next (lspdb, dnode);
+       lsp = dnode_get (dnode);
+-      lsp_destroy (lsp);
++      lsp_destroy (lsp, TRUE);
+       dict_delete_free (lspdb, dnode);
+       dnode = next;
+     }
+@@ -187,7 +208,7 @@ lsp_db_destroy (dict_t * lspdb)
+  * Remove all the frags belonging to the given lsp
+  */
+ static void
+-lsp_remove_frags (struct list *frags, dict_t * lspdb)
++lsp_remove_frags (struct list *frags, dict_t * lspdb, int clearnick)
+ {
+   dnode_t *dnode;
+   struct listnode *lnode, *lnnode;
+@@ -196,7 +217,7 @@ lsp_remove_frags (struct list *frags, dict_t * lspdb)
+   for (ALL_LIST_ELEMENTS (frags, lnode, lnnode, lsp))
+     {
+       dnode = dict_lookup (lspdb, lsp->lsp_header->lsp_id);
+-      lsp_destroy (lsp);
++      lsp_destroy (lsp, clearnick);
+       dnode_destroy (dict_delete (lspdb, dnode));
+     }
+ 
+@@ -222,7 +243,7 @@ lsp_search_and_destroy (u_char * id, dict_t * lspdb)
+       if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0)
+ 	{
+ 	  if (lsp->lspu.frags)
+-	    lsp_remove_frags (lsp->lspu.frags, lspdb);
++	    lsp_remove_frags (lsp->lspu.frags, lspdb, FALSE);
+ 	}
+       else
+ 	{
+@@ -232,7 +253,7 @@ lsp_search_and_destroy (u_char * id, dict_t * lspdb)
+ 	  if (lsp->lspu.zero_lsp && lsp->lspu.zero_lsp->lspu.frags)
+ 	    listnode_delete (lsp->lspu.zero_lsp->lspu.frags, lsp);
+ 	}
+-      lsp_destroy (lsp);
++      lsp_destroy (lsp, FALSE);
+       dnode_destroy (node);
+     }
+ }
+@@ -314,7 +335,7 @@ lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num)
+     newseq = seq_num++;
+ 
+   lsp->lsp_header->seq_num = htonl (newseq);
+-  fletcher_checksum (STREAM_DATA (lsp->pdu) + 12,
++  fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
+ 		   ntohs (lsp->lsp_header->pdu_len) - 12, 12);
+ 
+   return;
+@@ -367,6 +388,8 @@ lsp_update_data (struct isis_lsp *lsp, struct stream *stream,
+   int retval;
+ 
+   /* copying only the relevant part of our stream */
++  if (lsp->pdu != NULL)
++    stream_free (lsp->pdu);
+   lsp->pdu = stream_dup (stream);
+   
+   /* setting pointers to the correct place */
+@@ -414,6 +437,10 @@ lsp_update_data (struct isis_lsp *lsp, struct stream *stream,
+ 			   (lsp->lsp_header->lsp_bits & LSPBIT_IST));
+     }
+ 
++#ifdef HAVE_TRILL
++  if (isis->trill_active)
++    trill_parse_router_capability_tlvs (area, lsp);
++#endif
+ }
+ 
+ void
+@@ -428,7 +455,6 @@ lsp_update (struct isis_lsp *lsp, struct isis_link_state_hdr *lsp_hdr,
+     dnode_destroy (dict_delete (area->lspdb[level - 1], dnode));
+ 
+   /* free the old lsp data */
+-  XFREE (MTYPE_STREAM_DATA, lsp->pdu);
+   lsp_clear_data (lsp);
+ 
+   /* rebuild the lsp data */
+@@ -852,11 +878,9 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost)
+   if (lsp->tlv_data.te_is_neighs)
+     for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, lnode, te_is_neigh))
+     {
+-      uint32_t metric;
+-      memcpy (&metric, te_is_neigh->te_metric, 3);
+       lspid_print (te_is_neigh->neigh_id, LSPid, dynhost, 0);
+       vty_out (vty, "  Metric: %-10d IS-Extended %s%s",
+-	       ntohl (metric << 8), LSPid, VTY_NEWLINE);
++	       GET_TE_METRIC(te_is_neigh), LSPid, VTY_NEWLINE);
+     }
+ 
+   /* TE IPv4 tlv */
+@@ -933,16 +957,32 @@ lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to,
+   if (!FRAG_NEEDED (lsp->pdu, frag_thold, listcount (*from) * tlvsize + 2))
+     {
+       tlv_build_func (*from, lsp->pdu);
+-      *to = *from;
+-      *from = NULL;
++      if (listcount (*to) != 0)
++	{
++	  struct listnode *node, *nextnode;
++	  void *elem;
++
++	  for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem))
++	    {
++	      listnode_add (*to, elem);
++	      list_delete_node (*from, node);
++	    }
++	}
++      else
++	{
++	  list_free (*to);
++	  *to = *from;
++	  *from = NULL;
++	}
+     }
+   else if (!FRAG_NEEDED (lsp->pdu, frag_thold, tlvsize + 2))
+     {
+       /* fit all we can */
+       count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 -
+ 	(STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu));
+-      if (count)
+-	count = count / tlvsize;
++      count = count / tlvsize;
++      if (count > (int)listcount (*from))
++	count = listcount (*from);
+       for (i = 0; i < count; i++)
+ 	{
+ 	  listnode_add (*to, listgetdata (listhead (*from)));
+@@ -972,7 +1012,7 @@ lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area,
+       lsp_clear_data (lsp);
+       if (lsp0->tlv_data.auth_info.type)
+ 	{
+-	  memcpy (&lsp->tlv_data.auth_info, &lsp->tlv_data.auth_info,
++	  memcpy (&lsp->tlv_data.auth_info, &lsp0->tlv_data.auth_info,
+ 		  sizeof (struct isis_passwd));
+ 	  tlv_add_authinfo (lsp->tlv_data.auth_info.type,
+ 			    lsp->tlv_data.auth_info.len,
+@@ -991,7 +1031,7 @@ lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area,
+    */
+   if (lsp0->tlv_data.auth_info.type)
+     {
+-      memcpy (&lsp->tlv_data.auth_info, &lsp->tlv_data.auth_info,
++      memcpy (&lsp->tlv_data.auth_info, &lsp0->tlv_data.auth_info,
+ 	      sizeof (struct isis_passwd));
+       tlv_add_authinfo (lsp->tlv_data.auth_info.type,
+ 			lsp->tlv_data.auth_info.len,
+@@ -1094,6 +1134,11 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area)
+   if (lsp->tlv_data.area_addrs && listcount (lsp->tlv_data.area_addrs) > 0)
+     tlv_add_area_addrs (lsp->tlv_data.area_addrs, lsp->pdu);
+ 
++#ifdef HAVE_TRILL
++  if (isis->trill_active && CHECK_FLAG (area->trill->status, TRILL_NICK_SET))
++      tlv_add_trill_nickname (&(area->trill->nick), lsp->pdu, area);
++#endif
++
+   /* IPv4 address and TE router ID TLVs. In case of the first one we don't
+    * follow "C" vendor, but "J" vendor behavior - one IPv4 address is put into
+    * LSP and this address is same as router id. */
+@@ -1281,13 +1326,11 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area)
+ 		    memcpy (te_is_neigh->neigh_id,
+ 			    circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
+ 		  if (area->oldmetric)
+-		    metric =
+-		      ((htonl(circuit->metrics[level - 1].metric_default) >> 8)
+-			      & 0xffffff);
++		    metric = circuit->metrics[level - 1].metric_default;
+ 		  else
+-		    metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff);
++		    metric = circuit->te_metric[level - 1];
+ 
+-		  memcpy (te_is_neigh->te_metric, &metric, 3);
++		  SET_TE_METRIC(te_is_neigh, metric);
+ 		  listnode_add (tlv_data.te_is_neighs, te_is_neigh);
+ 		}
+ 	    }
+@@ -1320,8 +1363,8 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area)
+ 		  te_is_neigh = XCALLOC (MTYPE_ISIS_TLV,
+ 					 sizeof (struct te_is_neigh));
+ 		  memcpy (te_is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN);
+-		  metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff);
+-		  memcpy (te_is_neigh->te_metric, &metric, 3);
++		  metric = circuit->te_metric[level - 1];
++		  SET_TE_METRIC(te_is_neigh, metric);
+ 		  listnode_add (tlv_data.te_is_neighs, te_is_neigh);
+ 		}
+ 	    }
+@@ -1539,6 +1582,10 @@ lsp_non_pseudo_regenerate (struct isis_area *area, int level)
+   if (area->ipv6_circuits)
+     isis_spf_schedule6 (area, level);
+ #endif
++#ifdef HAVE_TRILL
++  if (isis->trill_active)
++    isis_spf_schedule_trill (area);
++#endif
+   return ISIS_OK;
+ }
+ 
+@@ -1803,7 +1850,7 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit,
+     tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu);
+ 
+   lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu));
+-  fletcher_checksum (STREAM_DATA (lsp->pdu) + 12,
++  fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
+ 		   ntohs (lsp->lsp_header->pdu_len) - 12, 12);
+ 
+   list_delete (adj_list);
+@@ -1811,7 +1858,7 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit,
+   return;
+ }
+ 
+-static int
++int
+ lsp_pseudo_regenerate (struct isis_circuit *circuit, int level)
+ {
+   dict_t *lspdb = circuit->area->lspdb[level - 1];
+@@ -2022,8 +2069,8 @@ lsp_tick (struct thread *thread)
+ 		  if (lsp->from_topology)
+ 		    THREAD_TIMER_OFF (lsp->t_lsp_top_ref);
+ #endif /* TOPOLOGY_GENERATE */
+-		  lsp_destroy (lsp);
+-		  dict_delete (area->lspdb[level], dnode);
++		  lsp_destroy (lsp, TRUE);
++		  dict_delete_free (area->lspdb[level], dnode);
+ 		}
+ 	      else if (flags_any_set (lsp->SRMflags))
+ 		listnode_add (lsp_list, lsp);
+@@ -2071,7 +2118,7 @@ lsp_purge_dr (u_char * id, struct isis_circuit *circuit, int level)
+       lsp->lsp_header->pdu_len =
+ 	htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN);
+       lsp->purged = 0;
+-      fletcher_checksum (STREAM_DATA (lsp->pdu) + 12,
++      fletcher_checksum(STREAM_DATA (lsp->pdu) + 12,
+ 		       ntohs (lsp->lsp_header->pdu_len) - 12, 12);
+       ISIS_FLAGS_SET_ALL (lsp->SRMflags);
+     }
+@@ -2227,7 +2274,7 @@ remove_topology_lsps (struct isis_area *area)
+       if (lsp->from_topology)
+ 	{
+ 	  THREAD_TIMER_OFF (lsp->t_lsp_top_ref);
+-	  lsp_destroy (lsp);
++	  lsp_destroy (lsp, TRUE);
+ 	  dict_delete (area->lspdb[0], dnode);
+ 	}
+       dnode = dnode_next;
+@@ -2325,8 +2372,6 @@ build_topology_lsp_data (struct isis_lsp *lsp, struct isis_area *area,
+ 
+       if (area->newmetric)
+ 	{
+-	  uint32_t metric;
+-
+ 	  if (tlv_data.te_is_neighs == NULL)
+ 	    {
+ 	      tlv_data.te_is_neighs = list_new ();
+@@ -2337,8 +2382,7 @@ build_topology_lsp_data (struct isis_lsp *lsp, struct isis_area *area,
+ 		  ISIS_SYS_ID_LEN);
+ 	  te_is_neigh->neigh_id[ISIS_SYS_ID_LEN - 1] = (to_lsp & 0xFF);
+ 	  te_is_neigh->neigh_id[ISIS_SYS_ID_LEN - 2] = ((to_lsp >> 8) & 0xFF);
+-	  metric = ((htonl(arc->distance) >> 8) & 0xffffff);
+-	  memcpy (te_is_neigh->te_metric, &metric, 3);
++	  SET_TE_METRIC(te_is_neigh, arc->distance);
+ 	  listnode_add (tlv_data.te_is_neighs, te_is_neigh);
+ 	}
+     }
+diff --git isisd/isis_lsp.h isisd/isis_lsp.h
+index adbde78..4112681 100644
+--- isisd/isis_lsp.h
++++ isisd/isis_lsp.h
+@@ -74,6 +74,7 @@ int lsp_refresh_l1 (struct thread *thread);
+ int lsp_refresh_l2 (struct thread *thread);
+ int lsp_regenerate_schedule (struct isis_area *area);
+ 
++int lsp_pseudo_regenerate (struct isis_circuit *circuit, int level);
+ int lsp_l1_pseudo_generate (struct isis_circuit *circuit);
+ int lsp_l2_pseudo_generate (struct isis_circuit *circuit);
+ int lsp_l1_refresh_pseudo (struct thread *thread);
+diff --git isisd/isis_main.c isisd/isis_main.c
+index 2411518..a75281d 100644
+--- isisd/isis_main.c
++++ isisd/isis_main.c
+@@ -43,6 +43,14 @@
+ #include "isisd/isis_circuit.h"
+ #include "isisd/isisd.h"
+ #include "isisd/isis_dynhn.h"
++#include "isisd/isis_spf.h"
++#include "isisd/isis_route.h"
++#include "isisd/isis_zebra.h"
++#ifdef HAVE_TRILL
++#include "isisd/isis_tlv.h"
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
++#endif
+ 
+ /* Default configuration file name */
+ #define ISISD_DEFAULT_CONFIG "isisd.conf"
+@@ -51,8 +59,12 @@
+ 
+ /* isisd privileges */
+ zebra_capabilities_t _caps_p[] = {
++#ifdef HAVE_TRILL
++  ZCAP_DL_CONFIG,
++#endif
+   ZCAP_NET_RAW,
+-  ZCAP_BIND
++  ZCAP_BIND,
++  ZCAP_EXEC
+ };
+ 
+ struct zebra_privs_t isisd_privs = {
+@@ -66,7 +78,7 @@ struct zebra_privs_t isisd_privs = {
+   .vty_group = VTY_GROUP,
+ #endif
+   .caps_p = _caps_p,
+-  .cap_num_p = 2,
++  .cap_num_p = sizeof (_caps_p) / sizeof (*_caps_p),
+   .cap_num_i = 0
+ };
+ 
+@@ -151,7 +163,10 @@ reload ()
+   zlog_debug ("Reload");
+   /* FIXME: Clean up func call here */
+   vty_reset ();
++  (void) isisd_privs.change (ZPRIVS_RAISE);
+   execve (_progpath, _argv, _envp);
++  zlog_err ("Reload failed: cannot exec %s: %s", _progpath,
++      safe_strerror (errno));
+ }
+ 
+ static void
+@@ -168,6 +183,9 @@ void
+ sighup (void)
+ {
+   zlog_debug ("SIGHUP received");
++#ifdef HAVE_TRILL
++  if (!trill_reload())
++#endif
+   reload ();
+ 
+   return;
+@@ -227,6 +245,11 @@ main (int argc, char **argv, char **envp)
+   char *vty_addr = NULL;
+   int dryrun = 0;
+ 
++#if defined(__sparc) && __GNUC__ == 3
++  /* work around alignment problems in gcc 3.x on SPARC */
++  asm("ta\t6");
++#endif
++
+   /* Get the programname without the preceding path. */
+   progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
+ 
+@@ -319,6 +342,22 @@ main (int argc, char **argv, char **envp)
+   memory_init ();
+   access_list_init();
+   isis_init ();
++  isis_circuit_init ();
++  isis_spf_cmds_init ();
++#ifdef HAVE_TRILL
++  install_trill_elements ();
++#endif
++
++  /* create the global 'isis' instance */
++  isis_new (0);
++
++#ifdef HAVE_TRILL
++  trill_read_config (&config_file, argc, argv);
++  /* we use the routing socket (zebra) only if TRILL is not enabled */
++  if (!isis->trill_active)
++#endif
++  isis_zebra_init ();
++
+   dyn_cache_init ();
+   sort_node ();
+ 
+@@ -337,7 +376,8 @@ main (int argc, char **argv, char **envp)
+     daemon (0, 0);
+ 
+   /* Process ID file creation. */
+-  pid_output (pid_file);
++  if (pid_file[0] != '\0')
++    pid_output (pid_file);
+ 
+   /* Make isis vty socket. */
+   vty_serv_sock (vty_addr, vty_port, ISIS_VTYSH_PATH);
+diff --git isisd/isis_misc.c isisd/isis_misc.c
+index 6b565bc..dbfb601 100644
+--- isisd/isis_misc.c
++++ isisd/isis_misc.c
+@@ -29,6 +29,7 @@
+ #include "if.h"
+ #include "command.h"
+ 
++#include "isisd/bool.h"
+ #include "isisd/dict.h"
+ #include "isisd/isis_constants.h"
+ #include "isisd/isis_common.h"
+@@ -38,8 +39,12 @@
+ 
+ #include "isisd/isis_tlv.h"
+ #include "isisd/isis_lsp.h"
++#include "isisd/isis_flags.h"
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
+ #include "isisd/isis_constants.h"
+ #include "isisd/isis_adjacency.h"
++#include "isisd/isis_dynhn.h"
+ 
+ /* staticly assigned vars for printing purposes */
+ struct in_addr new_prefix;
+@@ -99,10 +104,10 @@ isonet_print (u_char * from, int len)
+  * extract dot from the dotted str, and insert all the number in a buff 
+  */
+ int
+-dotformat2buff (u_char * buff, const u_char * dotted)
++dotformat2buff (u_char * buff, const char * dotted)
+ {
+   int dotlen, len = 0;
+-  const u_char *pos = dotted;
++  const char *pos = dotted;
+   u_char number[3];
+   int nextdotpos = 2;
+ 
+@@ -157,10 +162,10 @@ dotformat2buff (u_char * buff, const u_char * dotted)
+  * conversion of XXXX.XXXX.XXXX to memory
+  */
+ int
+-sysid2buff (u_char * buff, const u_char * dotted)
++sysid2buff (u_char * buff, const char * dotted)
+ {
+   int len = 0;
+-  const u_char *pos = dotted;
++  const char *pos = dotted;
+   u_char number[3];
+ 
+   number[2] = '\0';
+@@ -254,6 +259,11 @@ speaks (struct nlpids *nlpids, int family)
+ {
+   int i, speaks = 0;
+ 
++#ifdef HAVE_TRILL
++  /* TRILL has no nlpid defined */
++  if (family == AF_TRILL && isis->trill_active)
++    return TRUE;
++#endif
+   if (nlpids == (struct nlpids *) NULL)
+     return speaks;
+   for (i = 0; i < nlpids->count; i++)
+@@ -271,7 +281,7 @@ speaks (struct nlpids *nlpids, int family)
+  * Returns 0 on error, IS-IS Circuit Type on ok
+  */
+ int
+-string2circuit_t (const u_char * str)
++string2circuit_t (const char * str)
+ {
+ 
+   if (!str)
+@@ -498,7 +508,6 @@ unix_hostname (void)
+ {
+   static struct utsname names;
+   const char *hostname;
+-  extern struct host host;
+ 
+   hostname = host.name;
+   if (!hostname)
+@@ -509,3 +518,26 @@ unix_hostname (void)
+ 
+   return hostname;
+ }
++
++/*
++ * Returns the dynamic hostname associated with the passed system ID.
++ * If no dynamic hostname found then returns formatted system ID.
++ */
++const char *
++print_sys_hostname (u_char *sysid)
++{
++  struct isis_dynhn *dyn;
++
++  if (!sysid)
++    return "nullsysid";
++
++  /* For our system ID return our host name */
++  if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0)
++    return unix_hostname();
++
++  dyn = dynhn_find_by_id (sysid);
++  if (dyn)
++    return (const char *)dyn->name.name;
++
++  return sysid_print (sysid);
++}
+diff --git isisd/isis_misc.h isisd/isis_misc.h
+index d5003a8..2db48aa 100644
+--- isisd/isis_misc.h
++++ isisd/isis_misc.h
+@@ -24,7 +24,7 @@
+ #ifndef _ZEBRA_ISIS_MISC_H
+ #define _ZEBRA_ISIS_MISC_H
+ 
+-int string2circuit_t (const u_char *);
++int string2circuit_t (const char *);
+ const char *circuit_t2string (int);
+ const char *syst2string (int);
+ struct in_addr newprefix2inaddr (u_char * prefix_start,
+@@ -33,8 +33,8 @@ struct in_addr newprefix2inaddr (u_char * prefix_start,
+  * Converting input to memory stored format
+  * return value of 0 indicates wrong input
+  */
+-int dotformat2buff (u_char *, const u_char *);
+-int sysid2buff (u_char *, const u_char *);
++int dotformat2buff (u_char *, const char *);
++int sysid2buff (u_char *, const char *);
+ 
+ /*
+  * Printing functions
+@@ -46,6 +46,7 @@ const char *rawlspid_print (u_char *);
+ const char *time2string (u_int32_t);
+ /* typedef struct nlpids nlpids; */
+ char *nlpid2string (struct nlpids *);
++const char *print_sys_hostname (u_char *sysid);
+ 
+ /*
+  * misc functions
+diff --git isisd/isis_pdu.c isisd/isis_pdu.c
+index 4311a90..d8573aa 100644
+--- isisd/isis_pdu.c
++++ isisd/isis_pdu.c
+@@ -52,6 +52,11 @@
+ #include "isisd/iso_checksum.h"
+ #include "isisd/isis_csm.h"
+ #include "isisd/isis_events.h"
++#ifdef HAVE_TRILL
++#include <net/trill.h>
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
++#endif
+ 
+ extern struct thread_master *master;
+ extern struct isis *isis;
+@@ -787,9 +792,14 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa)
+ 
+   /*
+    * check if it's own interface ip match iih ip addrs
++   * If TRILL enabled bypass this check as IS-IS is used at layer-2
+    */
+-  if (!(found & TLVFLAG_IPV4_ADDR)
+-      || !ip_match (circuit->ip_addrs, tlvs.ipv4_addrs))
++  if (
++#ifdef HAVE_TRILL
++    !isis->trill_active &&
++#endif
++    (!(found & TLVFLAG_IPV4_ADDR)
++      || !ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)))
+     {
+       zlog_debug
+ 	("ISIS-Adj: No usable IP interface addresses in LAN IIH from %s\n",
+@@ -884,7 +894,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa)
+       if (adj->adj_state != ISIS_ADJ_UP)
+ 	{
+ 	  for (ALL_LIST_ELEMENTS_RO (tlvs.lan_neighs, node, snpa))
+-	    if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN))
++ 	    if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN))
+ 	    {
+ 	      isis_adj_state_change (adj, ISIS_ADJ_UP,
+ 				     "own SNPA found in LAN Neighbours TLV");
+@@ -892,6 +902,11 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa)
+ 	}
+     }
+ 
++#ifdef HAVE_TRILL
++  if (found & TLVFLAG_PORT_CAPABILITY && (tlvs.port_capabilities != NULL))
++    trill_process_hello(adj, tlvs.port_capabilities);
++#endif
++
+ out:
+   /* DEBUG_ADJ_PACKETS */
+   if (isis->debugs & DEBUG_ADJ_PACKETS)
+@@ -1054,6 +1069,7 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa)
+ 	      ((level == 2) &&
+ 	       (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL1)))
+ 	    return ISIS_WARNING;	/* Silently discard */
++	  adj = circuit->u.p2p.neighbor;
+ 	}
+     }
+ dontcheckadj:
+@@ -1251,7 +1267,12 @@ dontcheckadj:
+ 	}
+     }
+   if (lsp)
+-    lsp->adj = adj;
++    {
++      /* store the adjacency in LSP and add LSP to adj's LSP list */
++      lsp->adj = adj;
++      if (adj)
++        listnode_add (adj->lsps, lsp);  
++    }
+   return retval;
+ }
+ 
+@@ -1534,7 +1555,11 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit,
+ 	ISIS_SET_FLAG (lsp->SRMflags, circuit);
+       }
+       /* lets free it */
+-      list_free (lsp_list);
++      list_delete (lsp_list);
++
++#ifdef HAVE_TRILL
++      trill_lspdb_acquire_event (circuit, CSNPRCV);
++#endif
+     }
+ 
+   free_tlvs (&tlvs);
+@@ -1653,6 +1678,11 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa)
+ 
+   int retval = ISIS_OK;
+ 
++#ifdef HAVE_TRILL
++  if (isis->trill_active && circuit->vlans->rx_tci == TRILL_TCI_BPDU)
++    return trill_process_bpdu (circuit, ssnpa);
++#endif
++
+   /*
+    * Let's first read data from stream to the header
+    */
+@@ -1767,7 +1797,7 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa)
+   return retval;
+ }
+ 
+-#ifdef GNU_LINUX
++#if defined(GNU_LINUX) || defined(SUNOS_5)
+ int
+ isis_receive (struct thread *thread)
+ {
+@@ -1791,6 +1821,8 @@ isis_receive (struct thread *thread)
+ 
+   if (retval == ISIS_OK)
+     retval = isis_handle_pdu (circuit, ssnpa);
++  else
++    zlog_debug("isis_receive: error %d from circuit->rx", retval);
+ 
+   /* 
+    * prepare for next packet. 
+@@ -1879,6 +1911,10 @@ fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type)
+   hdr->id_len = 0;		/* ISIS_SYS_ID_LEN -  0==6 */
+   hdr->version2 = 1;
+   hdr->max_area_addrs = 0;	/* isis->max_area_addrs -  0==3 */
++#ifdef HAVE_TRILL
++  if (isis->trill_active)
++    hdr->max_area_addrs = isis->max_area_addrs;
++#endif
+ }
+ 
+ /*
+@@ -2017,6 +2053,12 @@ send_hello (struct isis_circuit *circuit, int level)
+       return ISIS_WARNING;
+ #endif /* HAVE_IPV6 */
+ 
++#ifdef HAVE_TRILL
++  if (level == TRILL_ISIS_LEVEL)
++    if (tlv_add_trill_vlans(circuit))
++      return ISIS_WARNING;
++#endif
++
+   if (circuit->u.bc.pad_hellos)
+     if (tlv_add_padding (circuit->snd_stream))
+       return ISIS_WARNING;
+@@ -2027,7 +2069,8 @@ send_hello (struct isis_circuit *circuit, int level)
+ 
+   retval = circuit->tx (circuit, level);
+   if (retval)
+-    zlog_warn ("sending of LAN Level %d Hello failed", level);
++    zlog_warn ("sending of LAN Level %d Hello failed on %s", level,
++      circuit->interface->name);
+ 
+   /* DEBUG_ADJ_PACKETS */
+   if (isis->debugs & DEBUG_ADJ_PACKETS)
+@@ -2070,7 +2113,12 @@ send_lan_l1_hello (struct thread *thread)
+   if (circuit->u.bc.run_dr_elect[0])
+     retval = isis_dr_elect (circuit, 1);
+ 
++#ifdef HAVE_TRILL
++  send_trill_vlan_hellos(circuit);
++  retval = ISIS_OK;
++#else
+   retval = send_lan_hello (circuit, 1);
++#endif
+ 
+   /* set next timer thread */
+   THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0],
+@@ -2252,6 +2300,9 @@ send_l1_csnp (struct thread *thread)
+   /* set next timer thread */
+   THREAD_TIMER_ON (master, circuit->t_send_csnp[0], send_l1_csnp, circuit,
+ 		   isis_jitter (circuit->csnp_interval[0], CSNP_JITTER));
++#ifdef HAVE_TRILL
++  trill_lspdb_acquire_event (circuit, CSNPSND);
++#endif
+ 
+   return retval;
+ }
+@@ -2274,6 +2325,9 @@ send_l2_csnp (struct thread *thread)
+   /* set next timer thread */
+   THREAD_TIMER_ON (master, circuit->t_send_csnp[1], send_l2_csnp, circuit,
+ 		   isis_jitter (circuit->csnp_interval[1], CSNP_JITTER));
++#ifdef HAVE_TRILL
++  trill_lspdb_acquire_event (circuit, CSNPSND);
++#endif
+ 
+   return retval;
+ }
+@@ -2399,6 +2453,10 @@ send_psnp (int level, struct isis_circuit *circuit)
+ 		}
+ 	    }
+ 	  list_delete (list);
++#ifdef HAVE_TRILL
++          if (circuit->circ_type != CIRCUIT_T_BROADCAST)
++	    trill_lspdb_acquire_event (circuit, PSNPSNDTRY);
++#endif
+ 	}
+     }
+ 
+diff --git isisd/isis_spf.c isisd/isis_spf.c
+index 5d7e9da..6e53601 100644
+--- isisd/isis_spf.c
++++ isisd/isis_spf.c
+@@ -37,21 +37,24 @@
+ #include "isis_constants.h"
+ #include "isis_common.h"
+ #include "dict.h"
++#include "isis_flags.h"
++#include "isis_circuit.h"
++#include "isis_tlv.h"
++#include "isis_lsp.h"
++#ifdef HAVE_TRILL
++#include "isis_vlans.h"
++#include "isis_trill.h"
++#endif
+ #include "isisd.h"
+ #include "isis_misc.h"
+ #include "isis_adjacency.h"
+-#include "isis_circuit.h"
+-#include "isis_tlv.h"
+ #include "isis_pdu.h"
+-#include "isis_lsp.h"
+ #include "isis_dynhn.h"
+ #include "isis_spf.h"
+ #include "isis_route.h"
+ #include "isis_csm.h"
+ 
+-extern struct isis *isis;
+ extern struct thread_master *master;
+-extern struct host host;
+ 
+ int isis_run_spf_l1 (struct thread *thread);
+ int isis_run_spf_l2 (struct thread *thread);
+@@ -188,7 +191,7 @@ vid2string (struct isis_vertex *vertex, u_char * buff)
+ }
+ #endif /* EXTREME_DEBUG */
+ 
+-static struct isis_spftree *
++struct isis_spftree *
+ isis_spftree_new ()
+ {
+   struct isis_spftree *tree;
+@@ -209,14 +212,14 @@ static void
+ isis_vertex_del (struct isis_vertex *vertex)
+ {
+   list_delete (vertex->Adj_N);
++  list_delete (vertex->children);
+ 
+   XFREE (MTYPE_ISIS_VERTEX, vertex);
+ 
+   return;
+ }
+ 
+-#if 0 /* HT: Not used yet. */
+-static void
++void
+ isis_spftree_del (struct isis_spftree *spftree)
+ {
+   spftree->tents->del = (void (*)(void *)) isis_vertex_del;
+@@ -229,7 +232,6 @@ isis_spftree_del (struct isis_spftree *spftree)
+ 
+   return;
+ }
+-#endif 
+ 
+ void
+ spftree_area_init (struct isis_area *area)
+@@ -258,6 +260,28 @@ spftree_area_init (struct isis_area *area)
+   return;
+ }
+ 
++void
++spftree_area_del (struct isis_area *area)
++{
++  if ((area->is_type & IS_LEVEL_1) && area->spftree[0] != NULL)
++    {
++      isis_spftree_del (area->spftree[0]);
++#ifdef HAVE_IPV6
++      isis_spftree_del (area->spftree6[0]);
++#endif
++    }
++
++  if ((area->is_type & IS_LEVEL_2) && area->spftree[1] != NULL)
++    {
++      isis_spftree_del (area->spftree[1]);
++#ifdef HAVE_IPV6
++      isis_spftree_del (area->spftree6[1]);
++#endif
++    }
++
++  return;
++}
++
+ static struct isis_vertex *
+ isis_vertex_new (void *id, enum vertextype vtype)
+ {
+@@ -297,36 +321,47 @@ isis_vertex_new (void *id, enum vertextype vtype)
+     }
+ 
+   vertex->Adj_N = list_new ();
++  vertex->children = list_new ();
+ 
+   return vertex;
+ }
+ 
++/* 
++ * Find the system LSP: returns the LSP in our LSP database 
++ * associated with the given system ID.
++ */
++static struct isis_lsp *
++isis_root_system_lsp (struct isis_area *area, int level, u_char *sysid)
++{
++  u_char lspid[ISIS_SYS_ID_LEN + 2];
++
++  memcpy (lspid, sysid, ISIS_SYS_ID_LEN);
++  LSP_PSEUDO_ID (lspid) = 0;
++  LSP_FRAGMENT (lspid) = 0;
++  return (lsp_search (lspid, area->lspdb[level - 1]));
++}
++
+ /*
+  * Add this IS to the root of SPT
+  */
+-static void
+-isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area,
+-		   int level)
++static struct isis_vertex *
++isis_spf_add_root (struct isis_spftree *spftree, struct isis_area *area,
++		   int level, u_char *sysid)
+ {
+   struct isis_vertex *vertex;
+   struct isis_lsp *lsp;
+-  u_char lspid[ISIS_SYS_ID_LEN + 2];
+ #ifdef EXTREME_DEBUG
+   u_char buff[BUFSIZ];
+ #endif /* EXTREME_DEBUG */
+-  memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN);
+-  LSP_PSEUDO_ID (lspid) = 0;
+-  LSP_FRAGMENT (lspid) = 0;
+-
+-  lsp = lsp_search (lspid, area->lspdb[level - 1]);
+ 
++  lsp = isis_root_system_lsp (area, level, sysid);
+   if (lsp == NULL)
+     zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level);
+ 
+   if (!area->oldmetric)
+-    vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_TE_IS);
++    vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_TE_IS);
+   else
+-    vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_IS);
++    vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_IS);
+ 
+   vertex->lsp = lsp;
+ 
+@@ -338,7 +373,7 @@ isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area,
+ 	      vertex->depth, vertex->d_N);
+ #endif /* EXTREME_DEBUG */
+ 
+-  return;
++  return vertex;
+ }
+ 
+ static struct isis_vertex *
+@@ -391,7 +426,8 @@ isis_find_vertex (struct list *list, void *id, enum vertextype vtype)
+ static struct isis_vertex *
+ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
+ 		   void *id, struct isis_adjacency *adj, u_int32_t cost,
+-		   int depth, int family)
++		   int depth, int family, struct isis_vertex *parent,
++		   struct isis_lsp *lsp)
+ {
+   struct isis_vertex *vertex, *v;
+   struct listnode *node;
+@@ -402,21 +438,28 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
+   vertex = isis_vertex_new (id, vtype);
+   vertex->d_N = cost;
+   vertex->depth = depth;
++  vertex->parent = parent;
++  if (parent && (listnode_lookup (parent->children, vertex) == NULL))
++    listnode_add (parent->children, vertex);
++  /* Store the LSP we learnt the vertex from */
++  vertex->lsp = lsp;
+ 
+   if (adj)
+     listnode_add (vertex->Adj_N, adj);
+ #ifdef EXTREME_DEBUG
+-  zlog_debug ("ISIS-Spf: add to TENT  %s %s depth %d dist %d",
++  zlog_debug ("ISIS-Spf: add to TENT %s %s %s depth %d dist %d",
++              print_sys_hostname (vertex->N.id),
+ 	      vtype2string (vertex->type), vid2string (vertex, buff),
+ 	      vertex->depth, vertex->d_N);
+ #endif /* EXTREME_DEBUG */
+-  listnode_add (spftree->tents, vertex);
++
+   if (list_isempty (spftree->tents))
+     {
+       listnode_add (spftree->tents, vertex);
+       return vertex;
+     }
+-  
++
++  listnode_add (spftree->tents, vertex);
+   /* XXX: This cant use the standard ALL_LIST_ELEMENT macro */
+   for (node = listhead (spftree->tents); node; node = listnextnode (node))
+     {
+@@ -454,7 +497,8 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
+ static struct isis_vertex *
+ isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype,
+ 		    void *id, struct isis_adjacency *adj, u_int32_t cost,
+-		    int family)
++		    int family, struct isis_vertex *parent,
++		    struct isis_lsp *lsp)
+ {
+   struct isis_vertex *vertex;
+ 
+@@ -482,13 +526,13 @@ isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype,
+     }
+ 
+ add2tent:
+-  return isis_spf_add2tent (spftree, vtype, id, adj, cost, 1, family);
++  return isis_spf_add2tent (spftree, vtype, id, adj, cost, 1, family, parent, lsp);
+ }
+ 
+ static void
+ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
+ 	   u_int16_t dist, u_int16_t depth, struct isis_adjacency *adj,
+-	   int family)
++	   int family, struct isis_vertex *parent, struct isis_lsp *lsp)
+ {
+   struct isis_vertex *vertex;
+ #ifdef EXTREME_DEBUG
+@@ -503,7 +547,8 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
+   if (vertex)
+     {
+ #ifdef EXTREME_DEBUG
+-      zlog_debug ("ISIS-Spf: process_N  %s %s dist %d already found from PATH",
++      zlog_debug ("ISIS-Spf: process_N %s %s %s dist %d already found from PATH",
++	          print_sys_hostname (vertex->N.id),
+ 		  vtype2string (vtype), vid2string (vertex, buff), dist);
+ #endif /* EXTREME_DEBUG */
+       assert (dist >= vertex->d_N);
+@@ -516,7 +561,8 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
+     {
+       /*        1) */
+ #ifdef EXTREME_DEBUG
+-      zlog_debug ("ISIS-Spf: process_N  %s %s dist %d",
++      zlog_debug ("ISIS-Spf: process_N %s %s %s dist %d",
++	          print_sys_hostname (vertex->N.id),
+ 		  vtype2string (vtype), vid2string (vertex, buff), dist);
+ #endif /* EXTREME_DEBUG */
+       if (vertex->d_N == dist)
+@@ -540,7 +586,13 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
+ 	}
+     }
+ 
+-  isis_spf_add2tent (spftree, vtype, id, adj, dist, depth, family);
++#ifdef EXTREME_DEBUG
++      zlog_debug ("ISIS-Spf: process_N add2tent %s %s dist %d",
++		  print_sys_hostname(id), 
++		  vtype2string (vtype), dist);
++#endif /* EXTREME_DEBUG */
++
++  isis_spf_add2tent (spftree, vtype, id, adj, dist, depth, family, parent, lsp);
+   return;
+ }
+ 
+@@ -549,7 +601,8 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
+  */
+ static int
+ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
+-		      uint32_t cost, uint16_t depth, int family)
++		      uint32_t cost, uint16_t depth, int family,
++		      u_char *root_sysid, struct isis_vertex *parent)
+ {
+   struct listnode *node, *fragnode = NULL;
+   u_int16_t dist;
+@@ -562,13 +615,27 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
+ #ifdef HAVE_IPV6
+   struct ipv6_reachability *ip6reach;
+ #endif /* HAVE_IPV6 */
++  struct isis_adjacency *adj = NULL;
++  static const u_char null_sysid[ISIS_SYS_ID_LEN];
+ 
+-
+-  if (!lsp->adj)
+-    return ISIS_WARNING;
+-  if (lsp->tlv_data.nlpids == NULL || !speaks (lsp->tlv_data.nlpids, family))
++  if (!speaks (lsp->tlv_data.nlpids, family))
+     return ISIS_OK;
+ 
++  /* 
++   * Note the adjacency (the neighboring system we received the LSP from) when computing 
++   * the SPF with our system as the root. Adjacencies computed are used for TRILL forwarding.
++   */
++  if (memcmp (root_sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0)
++    {
++      /* 
++       * lsp->adj can be NULL if computing SPF for other TRILL RBridges
++       * when lsp->adj is NULL during processing of lsps created by us.
++       */ 
++      if (!lsp->adj)
++        return ISIS_WARNING;
++      adj = lsp->adj;
++    }
++
+ lspfragloop:
+   if (lsp->lsp_header->seq_num == 0)
+     {
+@@ -577,6 +644,10 @@ lspfragloop:
+       return ISIS_WARNING;
+     }
+ 
++#ifdef EXTREME_DEBUG
++      zlog_debug ("ISIS-Spf: process_lsp %s", print_sys_hostname(lsp->lsp_header->lsp_id));
++#endif /* EXTREME_DEBUG */
++
+   if (!ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits))
+     {
+       if (lsp->tlv_data.is_neighs)
+@@ -585,13 +656,15 @@ lspfragloop:
+ 	    {
+ 	      /* C.2.6 a) */
+ 	      /* Two way connectivity */
+-	      if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
++	      if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
++		continue;
++	      if (!memcmp (is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN))
+ 		continue;
+ 	      dist = cost + is_neigh->metrics.metric_default;
+ 	      vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
+ 		: VTYPE_NONPSEUDO_IS;
+ 	      process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist,
+-			 depth + 1, lsp->adj, family);
++			 depth + 1, adj, family, parent, lsp);
+ 	    }
+ 	}
+       if (lsp->tlv_data.te_is_neighs)
+@@ -599,15 +672,15 @@ lspfragloop:
+ 	  for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node,
+ 				     te_is_neigh))
+ 	    {
+-	      uint32_t metric;
+-	      if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
++	      if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
++		continue;
++	      if (!memcmp (te_is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN))
+ 		continue;
+-	      memcpy (&metric, te_is_neigh->te_metric, 3);
+-	      dist = cost + ntohl (metric << 8);
++	      dist = cost + GET_TE_METRIC(te_is_neigh);
+ 	      vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS
+ 		: VTYPE_NONPSEUDO_TE_IS;
+ 	      process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist,
+-			 depth + 1, lsp->adj, family);
++			 depth + 1, adj, family, parent, lsp);
+ 	    }
+ 	}
+       if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs)
+@@ -621,7 +694,7 @@ lspfragloop:
+ 	      prefix.u.prefix4 = ipreach->prefix;
+ 	      prefix.prefixlen = ip_masklen (ipreach->mask);
+ 	      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
+-			 lsp->adj, family);
++			 adj, family, parent, lsp);
+ 	    }
+ 	}
+ 
+@@ -636,7 +709,7 @@ lspfragloop:
+ 	      prefix.u.prefix4 = ipreach->prefix;
+ 	      prefix.prefixlen = ip_masklen (ipreach->mask);
+ 	      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
+-			 lsp->adj, family);
++			 adj, family, parent, lsp);
+ 	    }
+ 	}
+       if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs)
+@@ -651,7 +724,7 @@ lspfragloop:
+ 						   te_ipv4_reach->control);
+ 	      prefix.prefixlen = (te_ipv4_reach->control & 0x3F);
+ 	      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
+-			 lsp->adj, family);
++			 adj, family, parent, lsp);
+ 	    }
+ 	}
+ #ifdef HAVE_IPV6
+@@ -668,7 +741,7 @@ lspfragloop:
+ 	      memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix,
+ 		      PSIZE (ip6reach->prefix_len));
+ 	      process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
+-			 lsp->adj, family);
++			 adj, family, parent, lsp);
+ 	    }
+ 	}
+ #endif /* HAVE_IPV6 */
+@@ -691,12 +764,22 @@ lspfragloop:
+ static int
+ isis_spf_process_pseudo_lsp (struct isis_spftree *spftree,
+ 			     struct isis_lsp *lsp, uint16_t cost,
+-			     uint16_t depth, int family)
++			     uint16_t depth, int family,
++			     u_char *root_sysid,
++			     struct isis_vertex *parent)
+ {
+   struct listnode *node, *fragnode = NULL;
+   struct is_neigh *is_neigh;
+   struct te_is_neigh *te_is_neigh;
+   enum vertextype vtype;
++  struct isis_adjacency *adj = NULL;
++
++  /* 
++   * Note the adjacency (the neighboring system we received the LSP from) when computing 
++   * the SPF with our system as the root. Adjacencies computed are used for TRILL forwarding.
++   */
++  if (memcmp (root_sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0)
++    adj = lsp->adj;
+ 
+ pseudofragloop:
+ 
+@@ -707,13 +790,17 @@ pseudofragloop:
+       return ISIS_WARNING;
+     }
+ 
++#ifdef EXTREME_DEBUG
++      zlog_debug ("ISIS-Spf: process_pseudo_lsp %s", print_sys_hostname(lsp->lsp_header->lsp_id));
++#endif /* EXTREME_DEBUG */
++
+   if (lsp->tlv_data.is_neighs)
+     for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh))
+       {
+ 	vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
+ 	  : VTYPE_NONPSEUDO_IS;
+ 	/* Two way connectivity */
+-	if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
++	if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
+ 	  continue;
+ 	if (isis_find_vertex
+ 	    (spftree->tents, (void *) is_neigh->neigh_id, vtype) == NULL
+@@ -721,8 +808,8 @@ pseudofragloop:
+ 			       vtype) == NULL)
+ 	  {
+ 	    /* C.2.5 i) */
+-	    isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, lsp->adj,
+-			     cost, depth, family);
++	    isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, adj,
++			     cost, depth, family, parent, lsp);
+ 	  }
+       }
+   if (lsp->tlv_data.te_is_neighs)
+@@ -731,7 +818,7 @@ pseudofragloop:
+ 	vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS
+ 	  : VTYPE_NONPSEUDO_TE_IS;
+ 	/* Two way connectivity */
+-	if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
++	if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN))
+ 	  continue;
+ 	if (isis_find_vertex
+ 	    (spftree->tents, (void *) te_is_neigh->neigh_id, vtype) == NULL
+@@ -739,8 +826,8 @@ pseudofragloop:
+ 				 vtype) == NULL)
+ 	  {
+ 	    /* C.2.5 i) */
+-	    isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, lsp->adj,
+-			       cost, depth, family);
++	    isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, adj,
++			       cost, depth, family, parent, lsp);
+ 	  }
+       }
+ 
+@@ -760,23 +847,67 @@ pseudofragloop:
+ 
+ static int
+ isis_spf_preload_tent (struct isis_spftree *spftree,
+-		       struct isis_area *area, int level, int family)
++		       struct isis_area *area, int level, 
++		       int family, u_char *root_sysid,
++		       struct isis_vertex *parent)
+ {
+   struct isis_vertex *vertex;
+   struct isis_circuit *circuit;
+   struct listnode *cnode, *anode, *ipnode;
+   struct isis_adjacency *adj;
+   struct isis_lsp *lsp;
++  struct isis_lsp *root_lsp;
+   struct list *adj_list;
+   struct list *adjdb;
+   struct prefix_ipv4 *ipv4;
+   struct prefix prefix;
+   int retval = ISIS_OK;
+   u_char lsp_id[ISIS_SYS_ID_LEN + 2];
++  static u_char null_lsp_id[ISIS_SYS_ID_LEN + 2];
+ #ifdef HAVE_IPV6
+   struct prefix_ipv6 *ipv6;
+ #endif /* HAVE_IPV6 */
+ 
++#ifdef HAVE_TRILL
++  /* 
++   * Check if computing SPF tree for another system. If computing SPF
++   * tree for another system (for TRILL) preload TENT by determining
++   * the neighboring systems of the root system by processing the root 
++   * system LSP.
++   */
++  if (isis->trill_active &&
++      memcmp (root_sysid, isis->sysid, ISIS_SYS_ID_LEN) != 0)
++    {
++      dnode_t *dnode;
++
++      memcpy (lsp_id, root_sysid, ISIS_SYS_ID_LEN);
++      LSP_PSEUDO_ID (lsp_id) = 0;
++      LSP_FRAGMENT (lsp_id) = 0;
++
++      /* should add at least one */
++      retval = ISIS_WARNING;
++      for (ALL_DICT_NODES_RO(area->lspdb[level-1], dnode, lsp))
++        {
++          if (LSP_FRAGMENT (lsp->lsp_header->lsp_id))
++            continue;
++          if (memcmp(lsp_id, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN) != 0)
++            continue;
++
++	  if (LSP_PSEUDO_ID (lsp->lsp_header->lsp_id))
++            retval = isis_spf_process_pseudo_lsp (spftree, lsp, 
++			    DEFAULT_CIRCUIT_METRICS, 0, AF_TRILL, 
++			    root_sysid, parent);
++	  else
++            retval = isis_spf_process_lsp (spftree, lsp,
++					   DEFAULT_CIRCUIT_METRICS, 1,
++					   AF_TRILL, root_sysid, parent);
++        }
++      return retval;
++    }
++#endif
++
++  root_lsp = isis_root_system_lsp (area, level, root_sysid);
++
+   for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
+     {
+       if (circuit->state != C_STATE_UP)
+@@ -800,7 +931,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
+ 	      prefix.u.prefix4 = ipv4->prefix;
+ 	      prefix.prefixlen = ipv4->prefixlen;
+ 	      isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix,
+-				  NULL, 0, family);
++				  NULL, 0, family, parent, root_lsp);
+ 	    }
+ 	}
+ #ifdef HAVE_IPV6
+@@ -812,7 +943,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
+ 	      prefix.prefixlen = ipv6->prefixlen;
+ 	      prefix.u.prefix6 = ipv6->prefix;
+ 	      isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL,
+-				  &prefix, NULL, 0, family);
++				  &prefix, NULL, 0, family, parent, root_lsp);
+ 	    }
+ 	}
+ #endif /* HAVE_IPV6 */
+@@ -845,21 +976,27 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
+ 		{
+ 		case ISIS_SYSTYPE_ES:
+ 		  isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
+-				      circuit->te_metric[level - 1], family);
++				      circuit->te_metric[level - 1],
++				      family, parent, root_lsp);
+ 		  break;
+ 		case ISIS_SYSTYPE_IS:
+ 		case ISIS_SYSTYPE_L1_IS:
+ 		case ISIS_SYSTYPE_L2_IS:
+ 		  vertex =
+-		    isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS,
++		    isis_spf_add_local (spftree,
++					area->oldmetric ? VTYPE_NONPSEUDO_IS :
++					VTYPE_NONPSEUDO_TE_IS,
+ 					adj->sysid, adj,
+-					circuit->te_metric[level - 1], family);
++					circuit->te_metric[level - 1],
++					family, parent, root_lsp);
+ 		  memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN);
+ 		  LSP_PSEUDO_ID (lsp_id) = 0;
+ 		  LSP_FRAGMENT (lsp_id) = 0;
+ 		  lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
+ 		  if (!lsp)
+-		    zlog_warn ("No lsp found for IS adjacency");
++		    zlog_warn ("No LSP %s found for IS adjacency L%d on %s (ID %u)",
++			rawlspid_print (lsp_id), level,
++			circuit->interface->name, circuit->circuit_id);
+ 		  /*          else {
+ 		     isis_spf_process_lsp (spftree, lsp, vertex->d_N, 1, family);
+ 		     } */
+@@ -878,22 +1015,34 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
+ 	    memcpy (lsp_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
+ 	  else
+ 	    memcpy (lsp_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
++	  /* can happen during DR reboot */
++	  if (memcmp (lsp_id, null_lsp_id, ISIS_SYS_ID_LEN + 1) == 0)
++	    {
++	      if (isis->debugs & DEBUG_SPF_EVENTS)
++		zlog_debug ("ISIS-Spf: no L%d DR on %s (ID %d)",
++		    level, circuit->interface->name, circuit->circuit_id);
++	      continue;
++	    }
+ 	  lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
+ 	  adj = isis_adj_lookup (lsp_id, adjdb);
+ 	  /* if no adj, we are the dis or error */
+ 	  if (!adj && !circuit->u.bc.is_dr[level - 1])
+ 	    {
+-	      zlog_warn ("ISIS-Spf: No adjacency found for DR");
++	      zlog_warn ("ISIS-Spf: No adjacency found for L%d DR SPF-root:%s on %s (ID %d)",
++		  level, print_sys_hostname(root_sysid),
++		  circuit->interface->name, circuit->circuit_id);
+ 	    }
+ 	  if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0)
+ 	    {
+-	      zlog_warn ("ISIS-Spf: No lsp found for DR");
++	      zlog_warn ("ISIS-Spf: No lsp found for L%d DR SPF-root:%s on %s (ID %d)",
++		  level, print_sys_hostname(root_sysid),
++		  circuit->interface->name, circuit->circuit_id);
+ 	    }
+ 	  else
+ 	    {
+ 	      isis_spf_process_pseudo_lsp (spftree, lsp,
+-				  circuit->te_metric[level - 1], 0, family);
+-
++				  circuit->te_metric[level - 1], 0,
++				  family, root_sysid, parent);
+ 	    }
+ 	}
+       else if (circuit->circ_type == CIRCUIT_T_P2P)
+@@ -905,19 +1054,22 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
+ 	    {
+ 	    case ISIS_SYSTYPE_ES:
+ 	      isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
+-				  circuit->te_metric[level - 1], family);
++				  circuit->te_metric[level - 1], family,
++				  parent, root_lsp);
+ 	      break;
+ 	    case ISIS_SYSTYPE_IS:
+ 	    case ISIS_SYSTYPE_L1_IS:
+ 	    case ISIS_SYSTYPE_L2_IS:
+ 	      if (speaks (&adj->nlpids, family))
+-		isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS, adj->sysid,
++		isis_spf_add_local (spftree,
++				    area->oldmetric ? VTYPE_NONPSEUDO_IS :
++				    VTYPE_NONPSEUDO_TE_IS, adj->sysid,
+ 				    adj, circuit->te_metric[level - 1],
+-				    family);
++				    family, parent, root_lsp);
+ 	      break;
+ 	    case ISIS_SYSTYPE_UNKNOWN:
+ 	    default:
+-	      zlog_warn ("isis_spf_preload_tent unknow adj type");
++	      zlog_warn ("isis_spf_preload_tent unknown adj type");
+ 	      break;
+ 	    }
+ 	}
+@@ -946,11 +1098,17 @@ add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex,
+   listnode_add (spftree->paths, vertex);
+ 
+ #ifdef EXTREME_DEBUG
+-  zlog_debug ("ISIS-Spf: added  %s %s depth %d dist %d to PATHS",
++  zlog_debug ("ISIS-Spf: added %s %s %s depth %d dist %d to PATHS",
++              print_sys_hostname (vertex->N.id),
+ 	      vtype2string (vertex->type), vid2string (vertex, buff),
+ 	      vertex->depth, vertex->d_N);
+ #endif /* EXTREME_DEBUG */
++
++#ifdef HAVE_TRILL
++  if (!isis->trill_active && vertex->type > VTYPE_ES)
++#else
+   if (vertex->type > VTYPE_ES)
++#endif
+     {
+       if (listcount (vertex->Adj_N) > 0)
+ 	isis_route_create ((struct prefix *) &vertex->N.prefix, vertex->d_N,
+@@ -969,16 +1127,17 @@ init_spt (struct isis_spftree *spftree)
+   list_delete_all_node (spftree->tents);
+   list_delete_all_node (spftree->paths);
+   spftree->tents->del = spftree->paths->del = NULL;
+-
+   return;
+ }
+ 
+ static int
+-isis_run_spf (struct isis_area *area, int level, int family)
++isis_run_spf (struct isis_area *area, int level, int family,
++		u_char *sysid, struct isis_spftree *calc_spftree)
+ {
+   int retval = ISIS_OK;
+   struct listnode *node;
+   struct isis_vertex *vertex;
++  struct isis_vertex *root_vertex;
+   struct isis_spftree *spftree = NULL;
+   u_char lsp_id[ISIS_SYS_ID_LEN + 2];
+   struct isis_lsp *lsp;
+@@ -986,47 +1145,63 @@ isis_run_spf (struct isis_area *area, int level, int family)
+   struct route_node *rode;
+   struct isis_route_info *rinfo;
+ 
+-  if (family == AF_INET)
++  if (calc_spftree)
++    spftree = calc_spftree;
++#ifdef HAVE_TRILL
++  else if (family == AF_TRILL)
++    spftree = area->spftree[level - 1];
++#endif
++  else if (family == AF_INET)
+     spftree = area->spftree[level - 1];
+ #ifdef HAVE_IPV6
+   else if (family == AF_INET6)
+     spftree = area->spftree6[level - 1];
+ #endif
+-
+   assert (spftree);
++  assert (sysid);
+ 
+-  /* Make all routes in current route table inactive. */
+-  if (family == AF_INET)
+-    table = area->route_table[level - 1];
++#ifdef HAVE_TRILL
++  if (family != AF_TRILL)
++#endif
++   {
++     /* Make all routes in current route table inactive. */
++     if (family == AF_INET)
++       table = area->route_table[level - 1];
+ #ifdef HAVE_IPV6
+-  else if (family == AF_INET6)
+-    table = area->route_table6[level - 1];
++     else if (family == AF_INET6)
++       table = area->route_table6[level - 1];
+ #endif
+ 
+-  for (rode = route_top (table); rode; rode = route_next (rode))
+-    {
+-      if (rode->info == NULL)
+-        continue;
+-      rinfo = rode->info;
++    for (rode = route_top (table); rode; rode = route_next (rode))
++      {
++        if (rode->info == NULL)
++          continue;
++        rinfo = rode->info;
+ 
+-      UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
+-    }
++        UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
++      }
++   }
+ 
+   /*
+    * C.2.5 Step 0
+    */
+   init_spt (spftree);
+   /*              a) */
+-  isis_spf_add_self (spftree, area, level);
++  root_vertex = isis_spf_add_root (spftree, area, level, sysid);
+   /*              b) */
+-  retval = isis_spf_preload_tent (spftree, area, level, family);
++  retval = isis_spf_preload_tent (spftree, area, level, family, sysid, root_vertex);
++  if (retval != ISIS_OK)
++    {
++      zlog_warn ("ISIS-Spf: failed to load TENT SPF-root:%s", print_sys_hostname(sysid));
++      goto out;
++    }
+ 
+   /*
+    * C.2.7 Step 2
+    */
+   if (listcount (spftree->tents) == 0)
+     {
+-      zlog_warn ("ISIS-Spf: TENT is empty");
++      zlog_warn ("ISIS-Spf: TENT is empty SPF-root:%s", print_sys_hostname(sysid));
+       goto out;
+     }
+ 
+@@ -1034,14 +1209,24 @@ isis_run_spf (struct isis_area *area, int level, int family)
+     {
+       node = listhead (spftree->tents);
+       vertex = listgetdata (node);
++
++#ifdef EXTREME_DEBUG
++  zlog_debug ("ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS",
++              print_sys_hostname (vertex->N.id),
++	      vtype2string (vertex->type), vertex->depth, vertex->d_N);
++#endif /* EXTREME_DEBUG */
++
+       /* Remove from tent list */
+       list_delete_node (spftree->tents, node);
+       if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type))
+ 	continue;
+       add_to_paths (spftree, vertex, area, level);
+-      if (vertex->type == VTYPE_PSEUDO_IS ||
+-	  vertex->type == VTYPE_NONPSEUDO_IS)
+-	{
++      switch (vertex->type)
++        {
++	case VTYPE_PSEUDO_IS:
++	case VTYPE_NONPSEUDO_IS:
++	case VTYPE_PSEUDO_TE_IS:
++	case VTYPE_NONPSEUDO_TE_IS:
+ 	  memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1);
+ 	  LSP_FRAGMENT (lsp_id) = 0;
+ 	  lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
+@@ -1050,13 +1235,12 @@ isis_run_spf (struct isis_area *area, int level, int family)
+ 	      if (LSP_PSEUDO_ID (lsp_id))
+ 		{
+ 		  isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N,
+-					       vertex->depth, family);
+-
++					       vertex->depth, family, sysid, vertex);
+ 		}
+ 	      else
+ 		{
+ 		  isis_spf_process_lsp (spftree, lsp, vertex->d_N,
+-					vertex->depth, family);
++					vertex->depth, family, sysid, vertex);
+ 		}
+ 	    }
+ 	  else
+@@ -1064,11 +1248,16 @@ isis_run_spf (struct isis_area *area, int level, int family)
+ 	      zlog_warn ("ISIS-Spf: No LSP found for %s",
+ 			 rawlspid_print (lsp_id));
+ 	    }
++	  break;
++	default:;
+ 	}
+     }
+ 
+ out:
+-  thread_add_event (master, isis_route_validate, area, 0);
++#ifdef HAVE_TRILL
++  if (family != AF_TRILL)
++#endif
++    thread_add_event (master, isis_route_validate, area, 0);
+   spftree->lastrun = time (NULL);
+   spftree->pending = 0;
+ 
+@@ -1098,7 +1287,7 @@ isis_run_spf_l1 (struct thread *thread)
+     zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
+ 
+   if (area->ip_circuits)
+-    retval = isis_run_spf (area, 1, AF_INET);
++    retval = isis_run_spf (area, 1, AF_INET, isis->sysid, NULL);
+ 
+   THREAD_TIMER_ON (master, area->spftree[0]->t_spf, isis_run_spf_l1, area,
+ 		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
+@@ -1128,7 +1317,7 @@ isis_run_spf_l2 (struct thread *thread)
+     zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF", area->area_tag);
+ 
+   if (area->ip_circuits)
+-    retval = isis_run_spf (area, 2, AF_INET);
++    retval = isis_run_spf (area, 2, AF_INET, isis->sysid, NULL);
+ 
+   THREAD_TIMER_ON (master, area->spftree[1]->t_spf, isis_run_spf_l2, area,
+ 		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
+@@ -1153,9 +1342,8 @@ isis_spf_schedule (struct isis_area *area, int level)
+     {
+       if (level == 1)
+ 	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, 60);
+-      else
+-	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, 60);
+-
++      else 
++	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, 60); 
+       spftree->pending = 1;
+       return retval;
+     }
+@@ -1176,7 +1364,9 @@ isis_spf_schedule (struct isis_area *area, int level)
+   else
+     {
+       spftree->pending = 0;
+-      retval = isis_run_spf (area, level, AF_INET);
++
++      retval = isis_run_spf (area, level, AF_INET, isis->sysid, NULL);
++
+       if (level == 1)
+ 	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area,
+ 			 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
+@@ -1211,7 +1401,7 @@ isis_run_spf6_l1 (struct thread *thread)
+     zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
+ 
+   if (area->ipv6_circuits)
+-    retval = isis_run_spf (area, 1, AF_INET6);
++    retval = isis_run_spf (area, 1, AF_INET6, isis->sysid, NULL);
+ 
+   THREAD_TIMER_ON (master, area->spftree6[0]->t_spf, isis_run_spf6_l1, area,
+ 		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
+@@ -1241,7 +1431,7 @@ isis_run_spf6_l2 (struct thread *thread)
+     zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF.", area->area_tag);
+ 
+   if (area->ipv6_circuits)
+-    retval = isis_run_spf (area, 2, AF_INET6);
++    retval = isis_run_spf (area, 2, AF_INET6, isis->sysid, NULL);
+ 
+   THREAD_TIMER_ON (master, area->spftree6[1]->t_spf, isis_run_spf6_l2, area,
+ 		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
+@@ -1289,7 +1479,7 @@ isis_spf_schedule6 (struct isis_area *area, int level)
+   else
+     {
+       spftree->pending = 0;
+-      retval = isis_run_spf (area, level, AF_INET6);
++      retval = isis_run_spf (area, level, AF_INET6, isis->sysid, NULL);
+ 
+       if (level == 1)
+ 	THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area,
+@@ -1303,51 +1493,174 @@ isis_spf_schedule6 (struct isis_area *area, int level)
+ }
+ #endif
+ 
++#ifdef HAVE_TRILL
++static int
++trill_complete_spf(struct isis_area *area)
++{
++  int retval;
++  dnode_t *dnode;
++  nicknode_t *tnode;
++
++  retval = isis_run_spf (area, TRILL_ISIS_LEVEL, AF_TRILL, isis->sysid, NULL);
++  if (retval != ISIS_OK)
++    zlog_warn ("ISIS-Spf running spf for system returned:%d", retval);
++
++  /* 
++   * Run SPF for all other RBridges in the campus as well to
++   * compute the distribution trees with other RBridges in
++   * the campus as root. 
++   */
++  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
++    {
++      retval = isis_run_spf (area, TRILL_ISIS_LEVEL, AF_TRILL,
++	                tnode->info.sysid, tnode->rdtree);
++      if (isis->debugs & DEBUG_SPF_EVENTS)
++        zlog_debug ("ISIS-Spf running spf for:%s",
++                        print_sys_hostname (tnode->info.sysid));
++      if (retval != ISIS_OK)
++        zlog_warn ("ISIS-Spf running spf for:%s returned:%d",
++                        print_sys_hostname (tnode->info.sysid), retval);
++    }
++
++  /* 
++   * Process computed SPF trees to create TRILL 
++   * forwarding and adjacency tables.
++   */
++  trill_process_spf (area);
++  return retval;
++}
++
++static int
++isis_run_spf_trill (struct thread *thread)
++{
++  struct isis_area *area;
++  int retval;
++
++  area = THREAD_ARG (thread);
++  assert (area);
++
++  area->spftree[0]->t_spf = NULL;
++
++  if (!(area->is_type & IS_LEVEL_1))
++    {
++      if (isis->debugs & DEBUG_SPF_EVENTS)
++	zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag);
++      return ISIS_WARNING;
++    }
++
++  if (isis->debugs & DEBUG_SPF_EVENTS)
++    zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
++
++  retval = trill_complete_spf(area);
++
++  THREAD_TIMER_ON (master, area->spftree[0]->t_spf, isis_run_spf_trill, area,
++		   isis_jitter (PERIODIC_SPF_INTERVAL, 10));
++
++  return retval;
++}
++
++int
++isis_spf_schedule_trill (struct isis_area *area)
++{
++  int retval = ISIS_OK;
++  struct isis_spftree *spftree = area->spftree[TRILL_ISIS_LEVEL - 1];
++  time_t diff, now = time (NULL);
++
++  if (spftree->pending)
++    return retval;
++
++  diff = now - spftree->lastrun;
++
++  /* FIXME: let's wait a minute before doing the SPF */
++  if (now - isis->uptime < 60 || isis->uptime == 0)
++    {
++      THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_trill, area, 60);
++
++      spftree->pending = 1;
++      return retval;
++    }
++
++  THREAD_TIMER_OFF (spftree->t_spf);
++
++  if (diff < MINIMUM_SPF_INTERVAL)
++    {
++      THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_trill, area,
++		       MINIMUM_SPF_INTERVAL - diff);
++
++      spftree->pending = 1;
++    }
++  else
++    {
++      spftree->pending = 0;
++
++      retval = trill_complete_spf(area);
++
++      THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_trill, area,
++		       isis_jitter (PERIODIC_SPF_INTERVAL, 10));
++    }
++
++  return retval;
++}
++#endif /* HAVE_TRILL */
++
+ static void
+-isis_print_paths (struct vty *vty, struct list *paths)
++isis_print_paths (struct vty *vty, struct list *paths, u_char *root_sysid)
+ {
+   struct listnode *node;
++  struct listnode *cnode;
+   struct isis_vertex *vertex;
+-  struct isis_dynhn *dyn, *nh_dyn = NULL;
++  struct isis_vertex *cvertex;
+   struct isis_adjacency *adj;
+ #if 0
+   u_char buff[255];
+ #endif /* 0 */
+ 
+   vty_out (vty, "System Id            Metric     Next-Hop"
+-	   "             Interface   SNPA%s", VTY_NEWLINE);
++	   "             Interface     SNPA        Tree%s", VTY_NEWLINE);
+ 
+   for (ALL_LIST_ELEMENTS_RO (paths, node, vertex))
+     {
+-      if (vertex->type != VTYPE_NONPSEUDO_IS)
++      if (vertex->type != VTYPE_NONPSEUDO_IS &&
++	  vertex->type != VTYPE_NONPSEUDO_TE_IS)
+ 	continue;
+-      if (memcmp (vertex->N.id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
++      if (memcmp (vertex->N.id, root_sysid, ISIS_SYS_ID_LEN) == 0)
+ 	{
+-	  vty_out (vty, "%s             --%s", host.name?host.name:"",
+-		   VTY_NEWLINE);
++	  vty_out (vty, "%-20s %-10s", print_sys_hostname (root_sysid), "--");
++	  vty_out (vty, "%-48s", "");
+ 	}
+       else
+ 	{
+-	  dyn = dynhn_find_by_id ((u_char *) vertex->N.id);
+-	  adj = listgetdata (listhead (vertex->Adj_N));
+-	  if (adj)
++	  if (listhead (vertex->Adj_N) &&
++	       (adj = listgetdata (listhead (vertex->Adj_N))))
+ 	    {
+-	      nh_dyn = dynhn_find_by_id (adj->sysid);
+-	      vty_out (vty, "%-20s %-10u %-20s %-11s %-5s%s",
+-		       (dyn != NULL) ? dyn->name.name :
+-		       (const u_char *)rawlspid_print ((u_char *) vertex->N.id),
+-		       vertex->d_N, (nh_dyn != NULL) ? nh_dyn->name.name :
+-		       (const u_char *)rawlspid_print (adj->sysid),
++	      vty_out (vty, "%-20s %-10u %-20s %-11s %-8s",
++                       print_sys_hostname (vertex->N.id),
++		       vertex->d_N, print_sys_hostname (adj->sysid),
+ 		       adj->circuit->interface->name,
+-		       snpa_print (adj->snpa), VTY_NEWLINE);
++		       snpa_print (adj->snpa));
+ 	    }
+ 	  else
+ 	    {
+-	      vty_out (vty, "%s              %u %s", dyn ? dyn->name.name :
+-		       (const u_char *) rawlspid_print (vertex->N.id),
+-		       vertex->d_N, VTY_NEWLINE);
++	      vty_out (vty, "%-20s %-10u %-48s ", 
++		      print_sys_hostname (vertex->N.id), 
++		      vertex->d_N, "");
+ 	    }
+ 	}
++
++      if (vertex->parent)
++        vty_out (vty, " %s(%d) :-> ",
++		print_sys_hostname (vertex->parent->N.id), vertex->type);
++      else
++        vty_out (vty, " :> ");
++
++      if (listcount (vertex->children) > 0)
++        {
++          for (ALL_LIST_ELEMENTS_RO (vertex->children, cnode, cvertex))
++	    vty_out (vty, "%s(%d),", 
++	         print_sys_hostname(cvertex->N.id), cvertex->type);
++	}
++      vty_out (vty, "%s", VTY_NEWLINE);
++
+ #if 0
+       vty_out (vty, "%s %s %u %s", vtype2string (vertex->type),
+ 	       vid2string (vertex, buff), vertex->d_N, VTY_NEWLINE);
+@@ -1355,6 +1668,26 @@ isis_print_paths (struct vty *vty, struct list *paths)
+     }
+ }
+ 
++#ifdef HAVE_TRILL
++static void
++trill_print_paths (struct vty *vty, struct isis_area *area)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++
++  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
++  {
++    if (tnode->rdtree && tnode->rdtree->paths->count > 0)
++    {
++      vty_out (vty, "%sRBridge distribution paths for RBridge:%s%s",
++       	       VTY_NEWLINE, print_sys_hostname (tnode->info.sysid),
++	       VTY_NEWLINE);
++      isis_print_paths (vty, tnode->rdtree->paths, tnode->info.sysid);
++    }
++  }
++}
++#endif /* HAVE_TRILL */
++
+ DEFUN (show_isis_topology,
+        show_isis_topology_cmd,
+        "show isis topology",
+@@ -1381,7 +1714,7 @@ DEFUN (show_isis_topology,
+ 	    {
+ 	      vty_out (vty, "IS-IS paths to level-%d routers that speak IP%s",
+ 		       level + 1, VTY_NEWLINE);
+-	      isis_print_paths (vty, area->spftree[level]->paths);
++	      isis_print_paths (vty, area->spftree[level]->paths, isis->sysid);
+ 	    }
+ #ifdef HAVE_IPV6
+ 	  if (area->ipv6_circuits > 0 && area->spftree6[level]
+@@ -1390,10 +1723,15 @@ DEFUN (show_isis_topology,
+ 	      vty_out (vty,
+ 		       "IS-IS paths to level-%d routers that speak IPv6%s",
+ 		       level + 1, VTY_NEWLINE);
+-	      isis_print_paths (vty, area->spftree6[level]->paths);
++	      isis_print_paths (vty, area->spftree6[level]->paths, isis->sysid);
+ 	    }
+ #endif /* HAVE_IPV6 */
+ 	}
++
++#ifdef HAVE_TRILL
++      if (isis->trill_active)
++	trill_print_paths (vty, area);
++#endif
+     }
+ 
+   return CMD_SUCCESS;
+@@ -1423,7 +1761,7 @@ DEFUN (show_isis_topology_l1,
+ 	{
+ 	  vty_out (vty, "IS-IS paths to level-1 routers that speak IP%s",
+ 		   VTY_NEWLINE);
+-	  isis_print_paths (vty, area->spftree[0]->paths);
++	  isis_print_paths (vty, area->spftree[0]->paths, isis->sysid);
+ 	}
+ #ifdef HAVE_IPV6
+       if (area->ipv6_circuits > 0 && area->spftree6[0]
+@@ -1431,9 +1769,13 @@ DEFUN (show_isis_topology_l1,
+ 	{
+ 	  vty_out (vty, "IS-IS paths to level-1 routers that speak IPv6%s",
+ 		   VTY_NEWLINE);
+-	  isis_print_paths (vty, area->spftree6[0]->paths);
++	  isis_print_paths (vty, area->spftree6[0]->paths, isis->sysid);
+ 	}
+ #endif /* HAVE_IPV6 */
++#ifdef HAVE_TRILL
++      if (isis->trill_active)
++	trill_print_paths (vty, area);
++#endif
+     }
+ 
+   return CMD_SUCCESS;
+@@ -1463,7 +1805,7 @@ DEFUN (show_isis_topology_l2,
+ 	{
+ 	  vty_out (vty, "IS-IS paths to level-2 routers that speak IP%s",
+ 		   VTY_NEWLINE);
+-	  isis_print_paths (vty, area->spftree[1]->paths);
++	  isis_print_paths (vty, area->spftree[1]->paths, isis->sysid);
+ 	}
+ #ifdef HAVE_IPV6
+       if (area->ipv6_circuits > 0 && area->spftree6[1]
+@@ -1471,7 +1813,7 @@ DEFUN (show_isis_topology_l2,
+ 	{
+ 	  vty_out (vty, "IS-IS paths to level-2 routers that speak IPv6%s",
+ 		   VTY_NEWLINE);
+-	  isis_print_paths (vty, area->spftree6[1]->paths);
++	  isis_print_paths (vty, area->spftree6[1]->paths, isis->sysid);
+ 	}
+ #endif /* HAVE_IPV6 */
+     }
+diff --git isisd/isis_spf.h isisd/isis_spf.h
+index 6bdab2d..ece9896 100644
+--- isisd/isis_spf.h
++++ isisd/isis_spf.h
+@@ -54,11 +54,12 @@ struct isis_vertex
+     struct prefix prefix;
+   } N;
+ 
+-  struct isis_lsp *lsp;
++  struct isis_lsp *lsp;         /* referring LSP (the LSP this vertex was learnt from) */
+   u_int32_t d_N;		/* d(N) Distance from this IS      */
+   u_int16_t depth;		/* The depth in the imaginary tree */
+-
+-  struct list *Adj_N;		/* {Adj(N)}  */
++  struct list *Adj_N;		/* {Adj(N)} next hop or neighbor list */
++  struct isis_vertex *parent;   /* parent and child links used to find adjacencies on tree */
++  struct list *children;
+ };
+ 
+ struct isis_spftree
+@@ -72,10 +73,16 @@ struct isis_spftree
+   u_int32_t timerun;		/* statistics */
+ };
+ 
++struct isis_spftree * isis_spftree_new (void);
++void isis_spftree_del (struct isis_spftree *spftree);
+ void spftree_area_init (struct isis_area *area);
++void spftree_area_del (struct isis_area *area);
+ int isis_spf_schedule (struct isis_area *area, int level);
+ void isis_spf_cmds_init (void);
+ #ifdef HAVE_IPV6
+ int isis_spf_schedule6 (struct isis_area *area, int level);
+ #endif
++#ifdef HAVE_TRILL
++int isis_spf_schedule_trill (struct isis_area *area);
++#endif
+ #endif /* _ZEBRA_ISIS_SPF_H */
+diff --git isisd/isis_tlv.c isisd/isis_tlv.c
+index 94fa65e..0690243 100644
+--- isisd/isis_tlv.c
++++ isisd/isis_tlv.c
+@@ -43,13 +43,6 @@
+ #include "isisd/isis_pdu.h"
+ #include "isisd/isis_lsp.h"
+ 
+-extern struct isis *isis;
+-
+-/*
+- * Prototypes.
+- */
+-int add_tlv (u_char, u_char, u_char *, struct stream *);
+-
+ void
+ free_tlv (void *val)
+ {
+@@ -93,7 +86,10 @@ free_tlvs (struct tlvs *tlvs)
+   if (tlvs->ipv6_reachs)
+     list_delete (tlvs->ipv6_reachs);
+ #endif /* HAVE_IPV6 */
+-  
++  if (tlvs->router_capabilities)
++    list_delete (tlvs->router_capabilities);
++  if (tlvs->port_capabilities)
++    list_delete (tlvs->port_capabilities);
+   return;
+ }
+ 
+@@ -714,6 +710,28 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
+ 	  pnt += length;
+ 	  break;
+ 
++        case ROUTER_CAPABILITY:
++	  /* +------+------+------+------+------+-------+
++	   * |Length|          Router ID        | Flags |
++	   * +------+------+------+------+------+-------+
++	   * |    optional sub-TLVs (0-250 octets)      |
++	   * +------+------+------+------+------+-------+
++	   */
++	  *found |= TLVFLAG_ROUTER_CAPABILITY;
++	  if (tlvs->router_capabilities == NULL)
++              tlvs->router_capabilities = list_new ();
++	  listnode_add (tlvs->router_capabilities, (pnt - 1));
++	  pnt += length;
++	  break;
++
++        case PORT_CAPABILITY:
++	  *found |= TLVFLAG_PORT_CAPABILITY;
++	  if (tlvs->port_capabilities == NULL)
++	      tlvs->port_capabilities  = list_new ();
++	  listnode_add (tlvs->port_capabilities, (pnt - 1));
++	  pnt += length;
++	  break;
++
+ 	default:
+ 	  zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d",
+ 		     areatag, type, length);
+@@ -731,7 +749,8 @@ int
+ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
+ {
+ 
+-  if (STREAM_SIZE (stream) - stream_get_endp (stream) < (unsigned) len + 2)
++  if (STREAM_SIZE (stream) - stream_get_endp (stream) <
++      (unsigned) len + TLFLDS_LEN)
+     {
+       zlog_warn ("No room for TLV of type %d", tag);
+       return ISIS_WARNING;
+@@ -739,7 +758,8 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
+ 
+   stream_putc (stream, tag);	/* TAG */
+   stream_putc (stream, len);	/* LENGTH */
+-  stream_put (stream, value, (int) len);	/* VALUE */
++  if (len > 0)
++    stream_put (stream, value, (int) len);	/* VALUE */
+ 
+ #ifdef EXTREME_DEBUG
+   zlog_debug ("Added TLV %d len %d", tag, len);
+@@ -747,6 +767,52 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
+   return ISIS_OK;
+ }
+ 
++/*
++ * Add a subTLV to an existing TLV.  Returns ISIS_ERROR if it can't fit in the
++ * stream at all.  Returns ISIS_WARNING if it doesn't fit in the current TLV
++ * (but may fit in another one).
++ */
++int
++add_subtlv (u_char tag, u_char len, u_char * value, size_t tlvpos,
++    struct stream *stream)
++{
++  unsigned newlen;
++
++  /* Compute new outer TLV length */
++  newlen = stream_getc_from(stream, tlvpos + 1) + (unsigned) len + TLFLDS_LEN;
++
++  /* Check if it's possible to fit the subTLV in the stream at all */
++  if (STREAM_SIZE (stream) - stream_get_endp (stream) <
++      (unsigned) len + TLFLDS_LEN ||
++      len > 255 - TLFLDS_LEN)
++    {
++      zlog_debug ("No room for subTLV %d len %d", tag, len);
++      return ISIS_ERROR;
++    }
++
++  /* Check if it'll fit in the current TLV */
++  if (newlen > 255)
++    {
++#ifdef EXTREME_DEBUG
++      /* extreme debug only, because repeating TLV is usually possible */
++      zlog_debug ("No room for subTLV %d len %d in TLV %d", tag, len,
++		  stream_getc_from(stream, tlvpos));
++#endif /* EXTREME DEBUG */
++      return ISIS_WARNING;
++    }
++
++  stream_putc (stream, tag);	/* TAG */
++  stream_putc (stream, len);	/* LENGTH */
++  stream_put (stream, value, (int) len);	/* VALUE */
++  stream_putc_at (stream,  tlvpos + 1, newlen);
++
++#ifdef EXTREME_DEBUG
++  zlog_debug ("Added subTLV %d len %d to TLV %d", tag, len,
++	      stream_getc_from(stream, tlvpos));
++#endif /* EXTREME DEBUG */
++  return ISIS_OK;
++}
++
+ int
+ tlv_add_area_addrs (struct list *area_addrs, struct stream *stream)
+ {
+diff --git isisd/isis_tlv.h isisd/isis_tlv.h
+index fc9f35f..f421627 100644
+--- isisd/isis_tlv.h
++++ isisd/isis_tlv.h
+@@ -60,6 +60,7 @@
+  * P2P Adjacency State      240   y   n   n  RFC3373
+  * IIH Sequence Number      241   y   n   n  draft-shen-isis-iih-sequence
+  * Router Capability        242   -   -   -  draft-ietf-isis-caps
++ * Port Capability	    243   n   y   n  draft-eastlake-trill-bridge-isis
+  *
+  * 
+  * IS Reachability sub-TLVs we (should) support.
+@@ -85,6 +86,28 @@
+  * 32bit administrative tag           1   draft-ietf-isis-admin-tags
+  * 64bit administrative tag           2   draft-ietf-isis-admin-tags
+  * Management prefix color          117   draft-ietf-isis-wg-multi-topology
++ *
++ *
++ * Router Capability sub-TLVs we support (Values TBD, temporary for now).
++ * ____________________________________________________________________________
++ * Name                           Value   Status
++ * ____________________________________________________________________________
++ * TRILL Flags			     21	  draft-ietf-trill-rbridge-protocol
++ * TRILL Nickname and Tree Root	     22	  draft-ietf-trill-rbridge-protocol
++ * TRILL Distribution Tree Roots     23	  draft-ietf-trill-rbridge-protocol
++ * TRILL VLANs and Bridge Roots	     24	  draft-ietf-trill-rbridge-protocol
++ * TRILL ESADI Participation	     25	  draft-ietf-trill-rbridge-protocol
++ * TRILL VLAN Groups		     26	  draft-ietf-trill-rbridge-protocol
++ * TRILL VLAN Mapping		     27	  draft-ietf-trill-rbridge-protocol
++ *
++ *
++ * Port Capability sub-TLVs we support
++ * ____________________________________________________________________________
++ * Name                           Value   Status
++ * ____________________________________________________________________________
++ * TRILL Special VLANs and Flags     10	  draft-ietf-trill-rbridge-protocol
++ * TRILL Enabled VLANs		     11	  draft-ietf-trill-rbridge-protocol
++ * TRILL Appointed Forwarders	     12	  draft-ietf-trill-rbridge-protocol
+  */
+ 
+ #define AREA_ADDRESSES            1
+@@ -109,12 +132,44 @@
+ #define IPV6_ADDR                 232
+ #define IPV6_REACHABILITY         236
+ #define WAY3_HELLO                240
++#define ROUTER_CAPABILITY	  242
++#define PORT_CAPABILITY		  243   /* TBD TRILL port capability TLV */
++
++/* ROUTER_CAPABILITY sub-TLVs for TRILL */
++#define	RCSTLV_TRILL_FLAGS	  21	/* TBD Flags */
++#define RCSTLV_TRILL_NICKNAME	  22	/* TBD Nickname and Tree Root */
++#define RCSTLV_TRILL_TREE_ROOTS	  23	/* TBD Distribution Tree Roots */
++#define RCSTLV_TRILL_VLANSROOTS	  24	/* TBD VLANs and Bridge Roots */
++#define RCSTLV_TRILL_ESADI	  25	/* TBD ESADI Participation */
++#define RCSTLV_TRILL_VLANGROUPS	  26	/* TBD VLAN Groups */
++#define RCSTLV_TRILL_VLANMAPPING  27	/* TBD VLAN Mapping */
++
++/* PORT_CAPABILITY sub-TLVs for TRILL */
++#define PCSTLV_VLANS		  10	/* Special VLANs and Flags */
++#define PCSTLV_ENABLEDVLANS	  11	/* Enabled VLANs */
++#define PCSTLV_APPFORWARDERS	  12	/* Appointed Forwarders */
+ 
+ #define IS_NEIGHBOURS_LEN (ISIS_SYS_ID_LEN + 5)
+ #define LAN_NEIGHBOURS_LEN 6
+ #define LSP_ENTRIES_LEN (10 + ISIS_SYS_ID_LEN)	/* FIXME: should be entry */
+ #define IPV4_REACH_LEN 12
+ #define IPV6_REACH_LEN 22
++#define TLFLDS_LEN 2			         /* Length of Type & Len 8-bit fields */	
++#define ROUTER_CAPABILITY_MIN_LEN  5		 /* Min len of router capability TLV */
++#define ROUTER_CAPABILITY_MAX_LEN  250 		 /* Max len of router capability TLV */
++
++/* TRILL Flags sub-TLV */
++#define TRILL_FLAGS_SUBTLV_MIN_LEN 1 		 /* Len of sub-TLV val */
++#define	TRILL_FLAGS_V0	0x80
++#define	TRILL_FLAGS_V1	0x40
++#define	TRILL_FLAGS_V2	0x20
++#define	TRILL_FLAGS_V3	0x10
++
++#define TRILL_NICKNAME_SUBTLV_MIN_LEN 7 	 /* Len of TRILL nickname sub-TLV value field */
++#define TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN 4    /* Len of variable len TRILL VLANs and Bridge Roots sub-TLV value field */
++#define PCSTLV_VLANS_LEN	 4		 /* Exact len of port capability VLANs sub-TLV */
++#define PCSTLV_VLANFWDERS_MIN_LEN 6		 /* Min. len of each appointed forwarders sub-TLV */
++#define PCSTLV_ENABLEDVLANS_MIN_LEN 3		 /* Min. len of enabled VLANS sub-TLV */
+ 
+ /* struct for neighbor */
+ struct is_neigh
+@@ -131,6 +186,15 @@ struct te_is_neigh
+   u_char sub_tlvs_length;
+ };
+ 
++/* Decode and encode three-octet metric into host byte order integer */
++#define GET_TE_METRIC(t) \
++  (((unsigned)(t)->te_metric[0]<<16) | ((t)->te_metric[1]<<8) | \
++   (t)->te_metric[2])
++#define SET_TE_METRIC(t, m) \
++  (((t)->te_metric[0] = (m) >> 16), \
++   ((t)->te_metric[1] = (m) >> 8), \
++   ((t)->te_metric[2] = (m)))
++
+ /* struct for es neighbors */
+ struct es_neigh
+ {
+@@ -213,7 +277,6 @@ struct ipv6_reachability
+   u_char prefix_len;
+   u_char prefix[16];
+ };
+-#endif /* HAVE_IPV6 */
+ 
+ /* bits in control_info */
+ #define CTRL_INFO_DIRECTION    0x80
+@@ -223,6 +286,92 @@ struct ipv6_reachability
+ #define DISTRIBUTION_INTERNAL  0
+ #define DISTRIBUTION_EXTERNAL  1
+ #define CTRL_INFO_SUBTLVS      0x20
++#endif /* HAVE_IPV6 */
++
++/* internal trill nickname struct */
++struct trill_nickname
++{
++  u_int16_t name;		/* network byte order */
++  u_int8_t priority;
++};
++
++/* Router Capability TLV: used in LSPs */
++struct router_capability_tlv
++{
++  u_char router_id[4];		   /* 4 octet Router ID */
++  u_int8_t flags;		   /* 1 octet flags */
++};
++
++/* internal router capability struct, includes tlv length */
++struct router_capability
++{
++  u_int8_t len;  		/* total length of the TLV */
++  struct router_capability_tlv rt_cap_tlv;
++};
++
++/* Port Capability TLV: used in Hellos */
++struct port_capability_tlv
++{
++  u_int8_t len;
++  u_int8_t value[1];
++};
++
++#ifdef __SUNPRO_C
++#pragma pack(1)
++#endif
++
++/* LSP: ROUTER_CAPABILITY RCSTLV_TRILL_NICKNAME */
++struct trill_nickname_subtlv
++{
++    u_int8_t tn_priority;
++    u_int16_t tn_nickname;
++    u_int16_t tn_trootpri;
++    u_int16_t tn_treecount;
++} __attribute__ ((packed));
++
++#ifdef __SUNPRO_C
++#pragma pack()
++#endif
++
++/* LSP: ROUTER_CAPABILITY RCSTLV_TRILL_VLANSROOTS */
++struct trill_vlan_bridge_roots_subtlv
++{
++    u_int16_t vlan_start;
++    u_int16_t vlan_end;
++};
++
++/* flags for vlan_start */
++#define	TVRFS_M4	0x8000
++#define	TVRFS_M6	0x4000
++#define	TVRFS_OM	0x2000
++#define	TVRFS_R		0x1000
++
++/* Hello: PORT_CAPABILITY PCSTLV_VLANS */
++struct trill_vlanflags_subtlv
++{
++    u_int16_t outer_vlan;
++    u_int16_t desig_vlan;
++};
++
++/* flags for outer_vlan */
++#define	TVFFO_AF	0x8000
++#define	TVFFO_AC	0x4000
++#define	TVFFO_VM	0x2000
++#define	TVFFO_R		0x1000
++
++/* Hello: PORT_CAPABILITY PCSTLV_APPFORWARDERS */
++struct appointed_vlanfwder_subtlv
++{
++    u_int16_t appointee_nick;
++    u_int16_t vlan_start;
++    u_int16_t vlan_end;
++};
++
++/* Hello: PORT_CAPABILITY PCSTLV_ENABLEDVLANS */
++struct trill_enabledvlans_subtlv
++{
++    u_int16_t start_vlan;
++};
+ 
+ /*
+  * Pointer to each tlv type, filled by parse_tlvs()
+@@ -249,6 +398,8 @@ struct tlvs
+   struct list *ipv6_reachs;
+ #endif
+   struct isis_passwd auth_info;
++  struct list *router_capabilities;
++  struct list *port_capabilities;
+ };
+ 
+ /*
+@@ -277,6 +428,8 @@ struct tlvs
+ #define TLVFLAG_TE_ROUTER_ID              (1<<19)
+ #define TLVFLAG_CHECKSUM                  (1<<20)
+ #define TLVFLAG_GRACEFUL_RESTART          (1<<21)
++#define TLVFLAG_ROUTER_CAPABILITY         (1<<22)
++#define TLVFLAG_PORT_CAPABILITY	          (1<<23)
+ 
+ void init_tlvs (struct tlvs *tlvs, uint32_t expected);
+ void free_tlvs (struct tlvs *tlvs);
+@@ -284,6 +437,11 @@ int parse_tlvs (char *areatag, u_char * stream, int size,
+ 		u_int32_t * expected, u_int32_t * found, struct tlvs *tlvs);
+ void free_tlv (void *val);
+ 
++int add_tlv (u_char, u_char, u_char *, struct stream *);
++int add_subtlv (u_char, u_char, u_char *, size_t, struct stream *);
++
++int tlv_add_trill_nickname (struct trill_nickname *nick_info, struct stream *stream,
++		struct isis_area *area);
+ int tlv_add_area_addrs (struct list *area_addrs, struct stream *stream);
+ int tlv_add_is_neighs (struct list *is_neighs, struct stream *stream);
+ int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream);
+@@ -304,6 +462,7 @@ int tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream);
+ int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream);
+ #endif /* HAVE_IPV6 */
+ 
++int tlv_add_trill_vlans(struct isis_circuit *);
+ int tlv_add_padding (struct stream *stream);
+ 
+ #endif /* _ZEBRA_ISIS_TLV_H */
+diff --git isisd/isis_trill.c isisd/isis_trill.c
+new file mode 100644
+index 0000000..3a38660
+--- /dev/null
++++ isisd/isis_trill.c
+@@ -0,0 +1,2346 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_trill.c
++ *
++ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++#include <zebra.h>
++#include <libdladm.h>
++#include <libdllink.h>
++#include <libdlbridge.h>
++#include <libdlvlan.h>
++#include <net/trill.h>
++
++#include "thread.h"
++#include "linklist.h"
++#include "stream.h"
++#include "vty.h"
++#include "log.h"
++#include "command.h"
++#include "memory.h"
++#include "prefix.h"
++#include "hash.h"
++#include "if.h"
++#include "table.h"
++#include "privs.h"
++
++#include "isisd/dict.h"
++#include "isisd/isis_common.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_flags.h"
++#include "isisd/isis_tlv.h"
++#include "isisd/isis_lsp.h"
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
++#include "isisd/isisd.h"
++#include "isisd/isis_misc.h"
++#include "isisd/isis_pdu.h"
++#include "isisd/isis_events.h"
++#include "isisd/bool.h"
++#include "isisd/isis_spf.h"
++#include "isisd/isis_adjacency.h"
++#include "isisd/isis_csm.h"
++
++extern struct zebra_privs_t isisd_privs;
++
++/* Number of available (randomly-assigned) nicknames, not counting reserved */
++static int nickavailcnt;
++
++/* Vector with bits set to indicate nicknames in use */
++static u_char nickbitvector[NICKNAMES_BITARRAY_SIZE];
++#define	NICK_IS_USED(n)		(nickbitvector[(n)/8] & (1<<((n)%8)))
++#define	NICK_SET_USED(n)	(nickbitvector[(n)/8] |= (1<<((n)%8)))
++#define	NICK_CLR_USED(n)	(nickbitvector[(n)/8] &= ~(1<<((n)%8)))
++
++/* Number of zero bits in each word of vector */
++static u_char clear_bit_count[CLEAR_BITARRAY_SIZE];
++
++static dladm_handle_t dlhandle;
++static char cfile_present = TRUE;
++
++static nickdb_search_result trill_search_rbridge (struct isis_area *, nickinfo_t *, dnode_t **);
++static void trill_dict_delete_nodes (dict_t *, dict_t *, void *, int);
++static int trill_nick_conflict(nickinfo_t *, nickinfo_t *);
++static int trill_parse_lsp (struct isis_lsp *, nickinfo_t *);
++
++/* Test and mark a nickname in host byte order as allocated or free */
++static int
++trill_nickname_nickbitmap_op(u_int16_t nick, int update, int val)
++{
++  if (nick == RBRIDGE_NICKNAME_NONE || nick == RBRIDGE_NICKNAME_UNUSED)
++    return FALSE;
++  if (val)
++    {
++      if (NICK_IS_USED(nick))
++	return TRUE;
++      if (!update)
++	return FALSE;
++      NICK_SET_USED(nick);
++      if (nick < RBRIDGE_NICKNAME_MINRES)
++	nickavailcnt--;
++      clear_bit_count[nick / CLEAR_BITARRAY_ENTRYLENBITS]--;
++    }
++  else
++    {
++      if (!NICK_IS_USED(nick))
++	return TRUE;
++      if (!update)
++	return FALSE;
++      NICK_CLR_USED(nick);
++      if (nick < RBRIDGE_NICKNAME_MINRES)
++	nickavailcnt++;
++      clear_bit_count[nick / CLEAR_BITARRAY_ENTRYLENBITS]++;
++    }
++  return FALSE;
++}
++
++/*
++ * trill_nickname_gen calls this function to randomly allocate a new nickname
++ * in host byte order. We also keep track of allocated and in-use nicks.
++ */
++static u_int16_t
++trill_nickname_alloc(void)
++{
++  u_int i, j, k;
++  u_int16_t nick;
++  u_int nicknum;
++  u_int freenickcnt = 0;
++
++  if (nickavailcnt < 1)
++    return RBRIDGE_NICKNAME_NONE;
++ 
++  /*
++   * Note that rand() usually returns 15 bits, so we overlap two values to make
++   * sure we're getting at least 16 bits (as long as rand() returns 8 bits or
++   * more).  Using random() instead would be better, but isis_main.c uses
++   * srand.
++   */
++  nicknum = ((rand() << 8) | rand()) % nickavailcnt;
++  for ( i = 0; i < sizeof (clear_bit_count); i++ )
++    {
++      freenickcnt += clear_bit_count[i];
++      if (freenickcnt <= nicknum)
++        continue;
++      nicknum -= freenickcnt - clear_bit_count[i];
++      nick = i * CLEAR_BITARRAY_ENTRYLEN * 8;
++      for ( j = 0; j < CLEAR_BITARRAY_ENTRYLEN; j++)
++	{
++	   for (k = 0; k < 8; k++, nick++)
++	     {
++		if (!NICK_IS_USED(nick) && nicknum-- == 0)
++		  {
++		    trill_nickname_nickbitmap_op (nick, TRUE, TRUE);
++		    return nick;
++		  }
++	     }
++	}
++      break;
++    }
++  assert (0);
++  return 0;
++}
++
++static void trill_nickname_reserve(u_int16_t nick_nbo)
++{
++  trill_nickname_nickbitmap_op(ntohs(nick_nbo), TRUE, TRUE);
++}
++
++static int is_nickname_used(u_int16_t nick_nbo)
++{
++  return trill_nickname_nickbitmap_op(ntohs(nick_nbo), FALSE, TRUE);
++}
++
++static void trill_nickname_free(u_int16_t nick_nbo)
++{
++  trill_nickname_nickbitmap_op(ntohs(nick_nbo), TRUE, FALSE);
++}
++
++static void
++trill_nickname_gen(struct isis_area *area)
++{
++  u_int16_t nick;
++
++  nick = trill_nickname_alloc();
++  if (nick == RBRIDGE_NICKNAME_NONE)
++    {
++      zlog_err("RBridge nickname allocation failed.  No nicknames available.");
++      abort();
++    }
++  else
++    {
++      area->trill->nick.name = htons(nick);
++      dladm_bridge_set_nick(area->trill->name, nick);
++      if (isis->debugs & DEBUG_TRILL_EVENTS)
++	zlog_debug("ISIS TRILL generated nick:%u", nick);
++    }
++}
++
++static int
++nick_cmp(const void *key1, const void *key2)
++{
++  return (memcmp(key1, key2, sizeof(u_int16_t)));
++}
++
++static int
++sysid_cmp(const void *key1, const void *key2)
++{
++  return (memcmp(key1, key2, ISIS_SYS_ID_LEN));
++}
++
++void
++trill_area_init(struct isis_area *area)
++{
++  u_int i;
++
++  area->trill->status = 0;
++  area->trill->nick.priority = DFLT_NICK_PRIORITY;
++  area->trill->root_priority = TRILL_DFLT_ROOT_PRIORITY;
++  area->trill->nickdb = dict_create(MAX_RBRIDGE_NODES, nick_cmp);
++  area->trill->sysidtonickdb = dict_create(MAX_RBRIDGE_NODES, sysid_cmp);
++
++  nickavailcnt = RBRIDGE_NICKNAME_MINRES - RBRIDGE_NICKNAME_NONE - 1;
++  memset(nickbitvector, 0, sizeof(nickbitvector));
++  for (i = 0; i < sizeof (clear_bit_count); i++)
++    clear_bit_count[i] = CLEAR_BITARRAY_ENTRYLENBITS;
++
++  /* These two are always reserved */
++  NICK_SET_USED(RBRIDGE_NICKNAME_NONE);
++  NICK_SET_USED(RBRIDGE_NICKNAME_UNUSED);
++  clear_bit_count[RBRIDGE_NICKNAME_NONE / CLEAR_BITARRAY_ENTRYLENBITS]--;
++  clear_bit_count[RBRIDGE_NICKNAME_UNUSED / CLEAR_BITARRAY_ENTRYLENBITS]--;
++
++  isis_event_system_type_change (area, TRILL_ISIS_LEVEL);
++  memset (area->trill->lspdb_acq_reqs, 0, sizeof(area->trill->lspdb_acq_reqs));
++}
++
++/*
++ * Called from isisd to handle trill nickname command.
++ * Nickname is user configured and in host byte order
++ */
++int
++trill_area_nickname(struct isis_area *area, u_int16_t nickname)
++{
++  int savednick;
++
++  if (nickname == RBRIDGE_NICKNAME_NONE)
++    {
++      /* Called from "no trill nickname" command */
++      trill_nickname_gen (area);
++      SET_FLAG (area->trill->status, TRILL_NICK_SET);
++      SET_FLAG (area->trill->status, TRILL_AUTONICK);
++      lsp_regenerate_schedule (area);
++      return TRUE;
++    }
++
++  nickname = htons(nickname);
++  savednick = area->trill->nick.name;
++  area->trill->nick.name = nickname;
++  area->trill->nick.priority |= CONFIGURED_NICK_PRIORITY;
++
++  /*
++   * Check if we know of another RBridge already using this nickname.
++   * If yes check if it conflicts with the nickname in the database.
++   */
++  if (is_nickname_used(nickname))
++    {
++      nickinfo_t ni;
++      dnode_t *dnode;
++      nicknode_t *tnode;
++
++      ni.nick = area->trill->nick;
++      memcpy(ni.sysid, isis->sysid, ISIS_SYS_ID_LEN);
++      if (trill_search_rbridge (area, &ni, &dnode) == FOUND)
++        {
++          assert (dnode);
++          tnode = dnode_get (dnode);
++          if (trill_nick_conflict (&(tnode->info), &ni))
++            {
++              trill_dict_delete_nodes (area->trill->nickdb,
++		     area->trill->sysidtonickdb, &nickname, FALSE);
++	    }
++	  else
++	    {
++              /* 
++	       * The other nick in our nickdb has greater priority so return
++	       * fail, restore nick and let user configure another nick.
++	       */
++               area->trill->nick.name = savednick; 
++	       area->trill->nick.priority &= ~CONFIGURED_NICK_PRIORITY;
++               return FALSE;
++	    }
++	}
++    }
++
++  trill_nickname_reserve(nickname);
++  SET_FLAG(area->trill->status, TRILL_NICK_SET);
++  UNSET_FLAG(area->trill->status, TRILL_AUTONICK);
++  lsp_regenerate_schedule (area);
++  return TRUE;
++}
++
++static void
++trill_nickname_priority_update(struct isis_area *area, u_int8_t priority)
++{
++  if (priority)
++    {
++      area->trill->nick.priority = priority;
++      SET_FLAG(area->trill->status, TRILL_PRIORITY_SET);
++    }
++  else
++    {
++      /* Called from "no trill nickname priority" command */
++      area->trill->nick.priority = DFLT_NICK_PRIORITY;
++      UNSET_FLAG(area->trill->status, TRILL_PRIORITY_SET);
++    }
++
++  /*
++   * Set the configured nickname priority bit if the
++   * nickname was not automatically generated. 
++   */
++  if (!CHECK_FLAG(area->trill->status, TRILL_AUTONICK))
++     area->trill->nick.priority |= CONFIGURED_NICK_PRIORITY;
++  lsp_regenerate_schedule (area);
++} 
++
++static void
++trill_nickinfo_del(nickinfo_t *ni)
++{
++  if (ni->dt_roots != NULL)
++    list_delete (ni->dt_roots);
++  if (ni->broots != NULL)
++    list_delete (ni->broots);
++}
++
++static void
++trill_dict_remnode ( dict_t *dict, dnode_t *dnode)
++{
++  nicknode_t *tnode;
++
++  assert (dnode);
++  tnode = dnode_get (dnode);
++  assert(tnode->refcnt);
++  tnode->refcnt--;
++  if (tnode->refcnt == 0)
++    {
++      isis_spftree_del (tnode->rdtree);
++      trill_nickinfo_del (&tnode->info);
++      if (tnode->adjnodes)
++        list_delete (tnode->adjnodes);
++      if (tnode->vlans_reachable)
++        list_delete (tnode->vlans_reachable);
++      XFREE (MTYPE_ISIS_TRILL_NICKDB_NODE, tnode);
++    }
++  dict_delete_free (dict, dnode);
++}
++
++static void
++trill_dict_free (dict_t *dict)
++{
++  dnode_t *dnode, *next;
++
++  dnode = dict_first (dict);
++  while (dnode)
++    {
++      next = dict_next (dict, dnode);
++      trill_dict_remnode (dict, dnode);
++      dnode = next;
++    }
++  dict_free_nodes (dict);
++  dict_destroy (dict);
++}
++
++void
++trill_area_free(struct isis_area *area)
++{
++  area->trill->status = 0;
++  trill_dict_free (area->trill->nickdb);
++  trill_dict_free (area->trill->sysidtonickdb);
++  if (area->trill->fwdtbl)
++    list_delete (area->trill->fwdtbl);
++  if (area->trill->adjnodes)
++    list_delete (area->trill->adjnodes);
++  if (area->trill->dt_roots)
++    list_delete (area->trill->dt_roots);
++  if (area->trill->vlans_reachable)
++    list_delete (area->trill->vlans_reachable);
++}
++
++/* 
++ * Delete nickname node in both databases. First a lookup
++ * of the node in first db by key1 and using the found node
++ * a lookup of the node in second db is done. Asserts the
++ * node if exists in one also exist in the second db.
++ */
++static void
++trill_dict_delete_nodes (dict_t *dict1, dict_t *dict2,
++		void *key1, int key2isnick)
++{
++  dnode_t *dnode1;
++  dnode_t *dnode2;
++  nicknode_t *tnode;
++  int nickname;
++
++  dnode1 = dict_lookup (dict1, key1);
++  if (dnode1)
++    {
++      tnode = (nicknode_t *) dnode_get(dnode1);
++      if (tnode)
++        {
++          if (key2isnick)
++	    {
++              dnode2 = dict_lookup (dict2, &(tnode->info.nick.name));
++              nickname = tnode->info.nick.name;
++	    }
++          else 
++            {
++              dnode2 = dict_lookup (dict2, tnode->info.sysid);
++	      nickname = *(int *)key1;
++	    }
++	  assert (dnode2);
++          trill_dict_remnode (dict2, dnode2);
++
++	  /* Mark the nickname as available */
++	  trill_nickname_free(nickname);
++	}
++      trill_dict_remnode (dict1, dnode1);
++    }
++}
++
++static void
++trill_update_nickinfo (nicknode_t *tnode, nickinfo_t *recvd_nick)
++{
++  trill_nickinfo_del(&tnode->info);
++  tnode->info = *recvd_nick;
++  /* clear copied nick */
++  memset(recvd_nick, 0, sizeof (*recvd_nick));
++}
++
++static void
++trill_dict_create_nodes (struct isis_area *area, nickinfo_t *nick)
++{
++  nicknode_t *tnode;
++
++  tnode = XCALLOC (MTYPE_ISIS_TRILL_NICKDB_NODE, sizeof(nicknode_t));
++  tnode->info = *nick;
++  dict_alloc_insert (area->trill->nickdb, &(tnode->info.nick.name), tnode);
++  tnode->refcnt = 1;
++  dict_alloc_insert (area->trill->sysidtonickdb, tnode->info.sysid, tnode);
++  tnode->refcnt++;
++  /* Mark the nickname as reserved */
++  trill_nickname_reserve(nick->nick.name);
++  tnode->rdtree = isis_spftree_new();
++  /* clear copied nick */
++  memset(nick, 0, sizeof (*nick));
++}
++
++/*
++ * Update nickname information in the dictionary objects.
++ */
++static void
++trill_nickdb_update ( struct isis_area *area, nickinfo_t *newnick)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++  nickdb_search_result res;
++
++  res = trill_search_rbridge (area, newnick, &dnode);
++  if (res == NOTFOUND) 
++    {
++      trill_dict_create_nodes (area, newnick);
++      return;
++    }
++
++  assert (dnode);
++  tnode = dnode_get (dnode);
++
++  /* If nickname & system ID of the node in our database match
++   * the nick received then we don't have to change any dictionary
++   * nodes. Update only the node information. Otherwise we update
++   * the dictionary nodes.
++   */
++  if (res == DUPLICATE || res == PRIORITY_CHANGE_ONLY)
++    {
++      trill_update_nickinfo (tnode, newnick);
++      return;
++    }
++
++  /*
++   * If the RBridge has a new nick then update its nick only.
++   */
++  if (res == NICK_CHANGED) 
++    {
++      if (isis->debugs & DEBUG_TRILL_EVENTS)
++        zlog_debug("ISIS TRILL storing new nick:%d from sysID:%s",
++	   ntohs(tnode->info.nick.name), sysid_print(tnode->info.sysid));
++
++      /* Delete the current nick in from our database */
++      trill_dict_delete_nodes (area->trill->sysidtonickdb,
++	      area->trill->nickdb, tnode->info.sysid, TRUE);
++      /* Store the new nick entry */
++      trill_dict_create_nodes (area, newnick);
++    }
++  else
++    {
++      /*
++       * There is another RBridge using the same nick.
++       * Determine which of the two RBridges should use the nick.
++       * But first we should delete any prev nick associated
++       * with system ID sending the newnick as it has just
++       * announced a new nick.
++       */
++      trill_dict_delete_nodes (area->trill->sysidtonickdb,
++	      area->trill->nickdb, newnick->sysid, TRUE);
++
++      if (trill_nick_conflict (&(tnode->info), newnick))
++        {
++          /* 
++	   * RBridge in tnode should choose another nick.
++	   * Delete tnode from our nickdb and store newnick.
++	   */
++          if (isis->debugs & DEBUG_TRILL_EVENTS)
++	    {
++              zlog_debug("ISIS TRILL replacing conflict nick:%d of sysID:%s",
++	        ntohs(tnode->info.nick.name), sysid_print(tnode->info.sysid));
++	      zlog_debug("ISIS TRILL .....with nick:%d of sysID:%s", 
++		ntohs(newnick->nick.name), sysid_print(newnick->sysid));
++	    }
++
++           trill_dict_delete_nodes (area->trill->sysidtonickdb,
++	      area->trill->nickdb, tnode->info.sysid, TRUE);
++           trill_dict_create_nodes (area, newnick);
++        }
++      else if (isis->debugs & DEBUG_TRILL_EVENTS)
++        {
++          zlog_debug("ISIS TRILL updated nick:%d of sysID:%s not accepted",
++		ntohs(newnick->nick.name), sysid_print(newnick->sysid));
++          zlog_debug("ISIS TRILL because of conflict with existing nick:%d of sysID:%s",
++		ntohs(tnode->info.nick.name), sysid_print(tnode->info.sysid));
++        }
++    }
++}
++
++/* 
++ * Search the nickname database and the sysidtonick database
++ * to see if we know a rbridge that matches either the passed nickname 
++ * or system ID or both. 
++ */
++static nickdb_search_result
++trill_search_rbridge ( struct isis_area *area, nickinfo_t *ni,
++		dnode_t **fndnode)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++
++  dnode = dict_lookup (area->trill->nickdb, &(ni->nick.name));
++  if (dnode == NULL) 
++    dnode = dict_lookup(area->trill->sysidtonickdb, ni->sysid);
++  if (dnode == NULL)
++    return NOTFOUND;
++
++  tnode = (nicknode_t *) dnode_get (dnode);
++  assert (tnode != NULL);
++  assert (tnode->refcnt);
++
++  if (fndnode)
++    *fndnode = dnode;
++  if ( memcmp(&(tnode->info.sysid), ni->sysid, ISIS_SYS_ID_LEN) != 0)
++    return FOUND;
++  if (tnode->info.nick.name != ni->nick.name)
++    return NICK_CHANGED;
++  if (tnode->info.nick.priority != ni->nick.priority)
++    return PRIORITY_CHANGE_ONLY;
++  /* Exact nick and sysid match */
++  return DUPLICATE; 
++}
++
++/*
++ * trill_nick_conflict: nickname conflict resolution fn
++ * Returns FALSE when nick1 has greater priority and
++ * returns TRUE when nick1 has lower priority and 
++ * must be changed.
++ */
++static int
++trill_nick_conflict(nickinfo_t *nick1, nickinfo_t *nick2)
++{
++  assert (nick1->nick.name == nick2->nick.name);
++
++  /* If nick1 priority is greater (or) 
++   * If priorities match & nick1 sysid is greater 
++   * then nick1 has higher priority
++   */
++  if ((nick1->nick.priority > nick2->nick.priority) ||
++      (nick1->nick.priority == nick2->nick.priority && 
++       (sysid_cmp (nick1->sysid, nick2->sysid) > 0)))
++    return FALSE;
++
++  return TRUE;
++}
++
++/*
++ * Remove nickname from the database.
++ * Called from lsp_destroy or when lsp is missing a nickname TLV.
++ */
++void
++trill_nick_destroy(struct isis_lsp *lsp)
++{
++  u_char *lsp_id;
++  nickinfo_t ni;
++  struct isis_area *area;
++  int delnick;
++
++  if (!isis->trill_active)
++    return;
++
++  area = listgetdata(listhead (isis->area_list));
++  lsp_id = lsp->lsp_header->lsp_id;
++
++  /* 
++   * If LSP is our own or is a Pseudonode LSP (and we do not
++   * learn nicks from Pseudonode LSPs) then no action is needed.
++   */
++  if ((memcmp (lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
++       || (LSP_PSEUDO_ID(lsp_id) != 0))
++    return;
++
++  if (!trill_parse_lsp (lsp, &ni) ||
++	  (ni.nick.name == RBRIDGE_NICKNAME_NONE))
++    {
++      /* Delete the nickname associated with the LSP system ID
++       * (if any) that did not include router capability TLV or
++       * TRILL flags or the nickname in the LSP is unknown. This
++       * happens when we recv a LSP from RBridge that just re-started
++       * and we have to delete the prev nick associated with it.
++       */
++      trill_dict_delete_nodes (area->trill->sysidtonickdb,
++	      area->trill->nickdb, lsp_id, TRUE);
++      if (isis->debugs & DEBUG_TRILL_EVENTS)
++        zlog_debug("ISIS TRILL removed any nickname associated"
++	      " with sysID:%s LSP seqnum:0x%08x pseudonode:%x",
++	      sysid_print(lsp_id), ntohl (lsp->lsp_header->seq_num),
++	      LSP_PSEUDO_ID(lsp_id) );
++      trill_nickinfo_del (&ni);
++      return;
++    }
++      
++  memcpy(ni.sysid, lsp_id, ISIS_SYS_ID_LEN);
++  delnick = ntohs(ni.nick.name);
++  if (delnick != RBRIDGE_NICKNAME_NONE &&
++      delnick != RBRIDGE_NICKNAME_UNUSED &&
++      ni.nick.priority >= MIN_RBRIDGE_PRIORITY)
++    {
++      /* Only delete if the nickname was learned
++       * from the LSP by ensuring both system ID
++       * and nickname in the LSP match with a node
++       * in our nick database.
++       */
++      if (trill_search_rbridge (area, &ni, NULL) == DUPLICATE)
++        {
++           trill_dict_delete_nodes (area->trill->sysidtonickdb,
++	      area->trill->nickdb, ni.sysid, TRUE);
++           if (isis->debugs & DEBUG_TRILL_EVENTS)
++              zlog_debug("ISIS TRILL removed nickname:%d associated"
++		" with sysID:%s LSP ID:0x%08x pseudonode:%x",
++		delnick, sysid_print(lsp_id),
++		ntohl (lsp->lsp_header->seq_num),
++	        LSP_PSEUDO_ID(lsp_id) );
++	}
++    }
++  else if (isis->debugs & DEBUG_TRILL_EVENTS)
++    zlog_debug("ISIS TRILL nick destroy invalid nickname:%d"
++        " from sysID:%s", delnick, sysid_print(lsp_id) );
++  trill_nickinfo_del (&ni);
++}
++
++static void
++trill_nick_recv(struct isis_area *area, nickinfo_t *other_nick)
++{
++  nickinfo_t ournick;
++  int nickchange = FALSE;
++
++  ournick.nick = area->trill->nick;
++  memcpy (ournick.sysid, area->isis->sysid, ISIS_SYS_ID_LEN);
++
++  if (isis->debugs & DEBUG_TRILL_EVENTS)
++    zlog_debug("ISIS TRILL nick recv:%d from sysID:%s",
++      ntohs (other_nick->nick.name), sysid_print(other_nick->sysid) );
++
++  /* Check for reserved TRILL nicknames that are not valid for use */
++  if ((other_nick->nick.name == RBRIDGE_NICKNAME_NONE) ||
++	  (other_nick->nick.name == RBRIDGE_NICKNAME_UNUSED)) 
++    {
++       zlog_warn("ISIS TRILL received reserved nickname:%d from sysID:%s",
++          ntohs (other_nick->nick.name), sysid_print(other_nick->sysid) );
++       return;
++    }
++
++  if (!(other_nick->flags & TRILL_FLAGS_V0))
++    {
++      zlog_info ("ISIS TRILL nick %d doesn't support V0 headers; flags %02X",
++	  ntohs (other_nick->nick.name), other_nick->flags);
++      return;
++    }
++
++  /* Check for conflict with our own nickname */
++  if (other_nick->nick.name == area->trill->nick.name)
++    {
++       /* Check if our nickname has lower priority or our
++	* system ID is lower, if not we keep our nickname.
++	*/
++       if (!(nickchange = trill_nick_conflict (&ournick, other_nick)))
++          return;
++    }
++
++  /* Update our nick database */
++  trill_nickdb_update (area, other_nick);
++
++  if (nickchange)
++     {
++       /* We choose another nickname */
++        trill_nickname_gen (area);
++        SET_FLAG(area->trill->status, TRILL_AUTONICK);
++
++	/* If previous nick was configured remove the bit 
++	 * indicating nickname was configured  (0x80) */
++	area->trill->nick.priority &= ~CONFIGURED_NICK_PRIORITY;
++
++	/* Regenerate our LSP to advertise the new nickname */
++	lsp_regenerate_schedule (area);
++
++        if (isis->debugs & DEBUG_TRILL_EVENTS)
++          zlog_debug("ISIS TRILL our nick changed to:%d",
++	    ntohs (area->trill->nick.name));
++     }
++}
++
++void
++trill_lspdb_acquire_event(struct isis_circuit *circuit, lspdbacq_state caller)
++{
++  struct isis_area *area;
++  u_int8_t cid;
++  struct listnode *cnode;
++  int done = TRUE;
++
++  area = circuit->area;
++  cid = circuit->circuit_id;
++
++  if (!isis->trill_active)
++    return;
++  if (CHECK_FLAG (area->trill->status, (TRILL_LSPDB_ACQUIRED | TRILL_NICK_SET)))
++    return;
++ 
++  switch(caller)
++    {
++    case CSNPRCV:
++    case CSNPSND:
++      LSPDB_ACQTRYINC (area, cid);
++      break;
++    case PSNPSNDTRY:
++      if (circuit->circ_type != CIRCUIT_T_BROADCAST)
++        LSPDB_ACQTRYINC (area, cid);
++      break;
++    default:
++      break;
++    }
++
++  for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
++    {
++      cid = circuit->circuit_id;
++
++      /* 
++       * If on any circuit we have reached max tries
++       * we consider LSP DB acquisition as done and
++       * assign ourselves a nickname
++       */
++      if (LSPDB_ACQTRYVAL (area, cid) > MAX_LSPDB_ACQTRIES)
++        {
++          done = TRUE;
++	  break;
++        }
++
++      /* 
++       * If on any circuits we haven't received min LSPDB update
++       * packets then we wait until we hit max tries above
++       * on any circuit. If not it can only mean there is no other
++       * IS-IS instance on any of our circuits and so we wait.
++       */
++      if (LSPDB_ACQTRYVAL (area, cid) < MIN_LSPDB_ACQTRIES)
++        done = FALSE;
++    }
++
++  if (isis->debugs & DEBUG_TRILL_EVENTS)
++    zlog_debug("ISIS TRILL LSPDB acquire event:%d cid:%d, done:%d",
++	    caller, cid, done);
++
++  if (done)
++    {
++      /* 
++       * LSP DB acquired state, sufficient to start
++       * advertising our nickname. Set flags, pick a
++       * new nick if necessary and trigger new LSPs with the nick.
++       */
++      SET_FLAG (area->trill->status, TRILL_LSPDB_ACQUIRED);
++      if (ntohs(area->trill->nick.name) == RBRIDGE_NICKNAME_NONE)
++	{
++	  trill_nickname_gen (area);
++	  SET_FLAG (area->trill->status, TRILL_NICK_SET);
++	  SET_FLAG (area->trill->status, TRILL_AUTONICK);
++	  lsp_regenerate_schedule (area);
++	}
++    }
++}
++
++static void
++trill_del_broot_node(void *data)
++{
++  struct trill_vlan_bridge_roots *broot = data;
++  if (broot->bridge_roots != NULL)
++    XFREE (MTYPE_ISIS_TRILL_BRIDGE_ROOTIDS, broot->bridge_roots);
++  XFREE (MTYPE_ISIS_TRILL_VLANBRIDGE_ROOTS, broot);
++}
++
++/*
++ * Returns TRUE if a nickname was received in the parsed LSP
++ */
++static int
++trill_parse_lsp (struct isis_lsp *lsp, nickinfo_t *recvd_nick)
++{
++  struct listnode *node;
++  struct router_capability *rtr_cap;
++  struct trill_vlan_bridge_roots *broot;
++  struct trill_vlan_bridge_roots_subtlv *brootstlv;
++  u_int8_t subtlvs_len;
++  u_int8_t subtlv;
++  u_int8_t subtlv_len;
++  u_int8_t stlvlen;
++  u_int16_t dtroot_nick;
++  int nick_recvd = FALSE;
++  int flags_recvd = FALSE;
++  int broots_recvd = FALSE;
++  u_char *pnt;
++  int idx;
++
++  memset(recvd_nick, 0, sizeof(nickinfo_t));
++  if (lsp->tlv_data.router_capabilities == NULL)
++    return FALSE;
++
++  memcpy (recvd_nick->sysid, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN);
++  recvd_nick->root_priority = TRILL_DFLT_ROOT_PRIORITY;
++
++  for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.router_capabilities, node, rtr_cap))
++    {
++       if (rtr_cap->len < ROUTER_CAPABILITY_MIN_LEN)
++         continue;
++
++       subtlvs_len = rtr_cap->len - ROUTER_CAPABILITY_MIN_LEN;
++       pnt = ((u_char *)rtr_cap) + sizeof(struct router_capability);
++       while (subtlvs_len >= TLFLDS_LEN)
++         {
++           subtlv = *(u_int8_t *)pnt++; subtlvs_len--;
++	   subtlv_len = *(u_int8_t *)pnt++; subtlvs_len--;
++	   if (subtlv_len > subtlvs_len)
++	     {
++               zlog_warn("ISIS trill_parse_lsp received invalid router"
++	         " capability subtlvs_len:%d subtlv_len:%d", 
++		 subtlvs_len, subtlv_len);
++               break;
++	     }
++
++	   switch (subtlv)
++	     {
++	     case RCSTLV_TRILL_FLAGS:
++	       /* var. len with min. one octet and must be included in each link state PDU */
++	       if (!flags_recvd && subtlv_len >= TRILL_FLAGS_SUBTLV_MIN_LEN)
++                 {
++	           recvd_nick->flags = *(u_int8_t *)pnt;
++	           flags_recvd = TRUE;
++		 }
++	       else
++	         { 
++		   if (flags_recvd)
++                     zlog_warn("ISIS trill_parse_lsp multiple TRILL"
++				    " flags sub-TLVs received");
++		   else
++                     zlog_warn("ISIS trill_parse_lsp invalid len:%d"
++				    " of TRILL flags sub-TLV", subtlv_len);
++		 }
++	       pnt += subtlv_len;
++	       subtlvs_len -= subtlv_len;
++	       break;
++
++	     case RCSTLV_TRILL_NICKNAME:
++	       stlvlen = subtlv_len;
++	       if (!nick_recvd && subtlv_len >= TRILL_NICKNAME_SUBTLV_MIN_LEN)
++                 {
++		   struct trill_nickname_subtlv *tn;
++
++		   tn = (struct trill_nickname_subtlv *)pnt;
++                   recvd_nick->nick.priority = tn->tn_priority;
++	           recvd_nick->nick.name = tn->tn_nickname;
++		   recvd_nick->root_priority = ntohs(tn->tn_trootpri);
++		   recvd_nick->root_count = ntohs(tn->tn_treecount);
++	           nick_recvd = TRUE;
++		 }
++	       else
++	         { 
++		   if (nick_recvd)
++                     zlog_warn("ISIS trill_parse_lsp multiple TRILL"
++				    " nick sub-TLVs received");
++		   else
++                     zlog_warn("ISIS trill_parse_lsp invalid len:%d"
++				    " of TRILL nick sub-TLV", subtlv_len);
++		 }
++               pnt += stlvlen;
++               subtlvs_len -= subtlv_len;
++	       break;
++
++	     case RCSTLV_TRILL_TREE_ROOTS:
++               if (subtlv_len % TRILL_NICKNAME_LEN)
++                 {
++                   pnt += subtlv_len;
++		   subtlvs_len -= subtlv_len;
++                   zlog_warn("ISIS trill_parse_lsp received invalid"
++		     " distribution tree roots subtlv_len:%d", subtlv_len);
++		   break;
++		 }
++	       if (recvd_nick->dt_roots == NULL)
++                 recvd_nick->dt_roots = list_new();
++	       stlvlen = subtlv_len;  /* zero len possible */
++	       while (stlvlen > 0)
++	         {
++                   dtroot_nick = *(u_int16_t *)pnt;
++		   pnt += TRILL_NICKNAME_LEN;
++		   subtlvs_len -= TRILL_NICKNAME_LEN;
++		   stlvlen -= TRILL_NICKNAME_LEN;
++
++                   if (dtroot_nick == RBRIDGE_NICKNAME_NONE ||
++		       dtroot_nick == RBRIDGE_NICKNAME_UNUSED)
++		     {
++                       zlog_warn("ISIS trill_parse_lsp received invalid"
++			 " distribution tree root nick:%d.", dtroot_nick);
++                       continue;
++		     }
++		   listnode_add (recvd_nick->dt_roots, (void *)(u_long)*(u_int16_t *)pnt);
++	         }
++	       break;
++
++	     case RCSTLV_TRILL_VLANSROOTS:
++	       if (subtlv_len < TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN)
++                 {
++                   pnt += subtlv_len;
++		   subtlvs_len -= subtlv_len;
++                   zlog_warn("ISIS trill_parse_lsp received invalid"
++		     " vlans and bridge roots subtlv_len:%d", subtlv_len);
++		   break;
++		 }
++
++	       if (recvd_nick->broots == NULL)
++                 {
++                   recvd_nick->broots = list_new();
++		   recvd_nick->broots->del = trill_del_broot_node;
++		 }
++
++	       broot = XCALLOC (MTYPE_ISIS_TRILL_VLANBRIDGE_ROOTS,
++		       sizeof(struct trill_vlan_bridge_roots));
++	       brootstlv = (struct trill_vlan_bridge_roots_subtlv *)pnt;
++	       broot->vlan_start = VLANTCI(ntohs(brootstlv->vlan_start));
++	       broot->vlan_end = VLANTCI(ntohs(brootstlv->vlan_end));
++	       pnt += TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN;
++	       subtlvs_len -= TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN;
++	       subtlv_len -= TRILL_VLANSNBRIROOTS_SUBTLV_MIN_LEN;
++	       if (subtlv_len % ETHERADDRL)
++                 {
++                   pnt += subtlv_len;
++		   subtlvs_len -= subtlv_len;
++                   zlog_warn("ISIS trill_parse_lsp received invalid"
++		     " vlans and bridge roots subtlv_len:%d", subtlv_len);
++		   XFREE (MTYPE_ISIS_TRILL_VLANBRIDGE_ROOTS, broot);
++		   break;
++		 }
++
++	       if (subtlv_len > 0)
++                 {
++	            broot->bridge_roots_count = subtlv_len / ETHERADDRL;
++	            broot->bridge_roots = XMALLOC (MTYPE_ISIS_TRILL_BRIDGE_ROOTIDS, subtlv_len);
++	            memcpy(broot->bridge_roots, pnt, subtlv_len);
++	            pnt += subtlv_len;
++		 }
++	       subtlvs_len -= subtlv_len;
++	       listnode_add (recvd_nick->broots, broot);
++	       broots_recvd = TRUE;
++	       break;
++
++	     default:
++	       pnt += subtlv_len;
++	       subtlvs_len -= subtlv_len;
++	       break;
++	     }
++         }
++    }
++
++  if (recvd_nick->broots != NULL && broots_recvd == TRUE)
++    {
++      for (ALL_LIST_ELEMENTS_RO(recvd_nick->broots, node, broot))
++        {
++           for (idx=broot->vlan_start; idx <=broot->vlan_end; idx++)
++	      SET_VLAN(recvd_nick->vlans_forwarder, idx);
++        }
++    }
++  return (nick_recvd);
++}
++
++void
++trill_parse_router_capability_tlvs (struct isis_area *area,
++		struct isis_lsp *lsp)
++{
++  nickinfo_t recvd_nick;
++
++  /* Return if LSP is our own or is a pseudonode LSP */
++  if ((memcmp (lsp->lsp_header->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
++       || (LSP_PSEUDO_ID(lsp->lsp_header->lsp_id) != 0))
++    return;
++
++  if (trill_parse_lsp (lsp, &recvd_nick))
++    {
++      /* Parsed LSP correctly but process only if nick is not unknown */
++      if (recvd_nick.nick.name != RBRIDGE_NICKNAME_NONE)
++         trill_nick_recv (area, &recvd_nick);
++    }
++  else 
++    {
++       /* if we have a nickname stored from this RBridge we remove it as this 
++	* LSP without a nickname likely indicates the RBridge has re-started 
++	* and hasn't chosen a new nick.
++        */
++       trill_nick_destroy (lsp);
++    }
++
++  trill_nickinfo_del (&recvd_nick);
++}
++
++/* Lookup nickname when given a system ID */
++u_int16_t
++sysid_to_nick(struct isis_area *area, u_char *sysid)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++
++  dnode = dict_lookup (area->trill->sysidtonickdb, sysid);
++  if (dnode == NULL)
++    return 0;
++  tnode = (nicknode_t *) dnode_get (dnode);
++  return tnode->info.nick.name;
++}
++
++nicknode_t *
++trill_nicknode_lookup(struct isis_area *area, uint16_t nick)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++
++  dnode = dict_lookup (area->trill->nickdb, &nick);
++  if (dnode == NULL) 
++    return (NULL);
++  tnode = (nicknode_t *) dnode_get (dnode);
++  return (tnode);
++}
++
++/* Lookup system ID when given a nickname */
++u_char *
++nick_to_sysid(struct isis_area *area, u_int16_t nick) 
++{
++  nicknode_t *tnode;
++
++  tnode = trill_nicknode_lookup(area, nick);
++  if (tnode == NULL)
++    return (NULL);
++  return tnode->info.sysid;
++}
++
++static void
++trill_destroy_nickfwdtable(void *obj)
++{
++  XFREE (MTYPE_ISIS_TRILL_FWDTBL_NODE, obj);
++}
++
++/*
++ * Creates a nickname forwarding table for TRILL. 
++ * Forwarding table is stored in the per-area fwdtbl list. 
++ */
++static void
++trill_create_nickfwdtable(struct isis_area *area)
++{
++  struct listnode *node;
++  struct isis_vertex *vertex;
++  struct isis_adjacency *adj;
++  struct list *fwdlist = NULL;
++  nickfwdtblnode_t *fwdnode;
++  struct isis_spftree *rdtree;
++  int firstnode = TRUE; 
++
++  rdtree = area->spftree [TRILL_ISIS_LEVEL-1];
++  if (area->trill->fwdtbl)
++    list_delete (area->trill->fwdtbl);
++  area->trill->fwdtbl = NULL;
++
++  for (ALL_LIST_ELEMENTS_RO (rdtree->paths, node, vertex))
++    {
++      if (firstnode)
++        {
++          /* first node in path list is us */
++          fwdlist = list_new();
++	  fwdlist->del = trill_destroy_nickfwdtable;
++	  firstnode = FALSE;
++	  continue;
++        }
++      if (vertex->type != VTYPE_NONPSEUDO_IS &&
++	  vertex->type != VTYPE_NONPSEUDO_TE_IS)
++	continue;
++
++      if (listhead (vertex->Adj_N) && 
++           (adj = listgetdata (listhead (vertex->Adj_N))))
++        {
++          fwdnode = XCALLOC (MTYPE_ISIS_TRILL_FWDTBL_NODE, sizeof(nickfwdtblnode_t));
++          fwdnode->dest_nick = sysid_to_nick (area, vertex->N.id);
++          memcpy(fwdnode->adj_snpa, adj->snpa, sizeof(fwdnode->adj_snpa));
++	  fwdnode->interface = adj->circuit->interface;
++          listnode_add (fwdlist, fwdnode);
++        }
++      else
++        {
++          list_delete (fwdlist);
++	  fwdlist = NULL;
++	  return;
++	}
++    }
++
++  area->trill->fwdtbl = fwdlist;
++}
++
++static void
++trill_fwdtbl_print (struct vty *vty, struct isis_area *area)
++{ 
++  struct listnode *node;
++  nickfwdtblnode_t *fwdnode;
++
++  if (area->trill->fwdtbl == NULL)
++    return;
++
++  vty_out(vty, "RBridge        nickname   interface  nexthop MAC%s", VTY_NEWLINE); 
++  for (ALL_LIST_ELEMENTS_RO (area->trill->fwdtbl, node, fwdnode))
++    {
++      vty_out (vty, "%-15s   %-5d      %-5s  %-15s%s",
++               print_sys_hostname (nick_to_sysid (area, fwdnode->dest_nick)),
++	       ntohs (fwdnode->dest_nick), fwdnode->interface->name,
++	       snpa_print (fwdnode->adj_snpa), VTY_NEWLINE);
++    }
++}
++
++static void
++trill_add_nickadjlist(struct isis_area *area, struct list *adjlist,
++		 struct isis_vertex *vertex)
++{
++  u_int16_t nick;
++
++  nick = sysid_to_nick (area, vertex->N.id);
++  if (!nick)
++    return;
++  if (listnode_lookup (adjlist, (void *)(u_long)nick) != NULL)
++    return;
++  listnode_add (adjlist, (void *)(u_long)nick);
++}
++
++/*
++ * Creates TRILL nickname adjacency lists for each distribution tree (DT).
++ * An adjacency list consists of our (this RBridge) adjacent nodes in the 
++ * campus that are present on the DT paths. It is a subset of our adjacent 
++ * nodes. The adjacency list for a distribution tree is stored inside the 
++ * root dict node of the distribution tree in our nickname database.
++ */
++static void
++trill_create_nickadjlist(struct isis_area *area, nicknode_t *nicknode)
++{
++  struct listnode *node;
++  struct listnode *cnode;
++  struct isis_vertex *vertex;
++  struct isis_vertex *cvertex;
++  struct isis_vertex *rbvertex = NULL;
++  struct list *adjlist;
++  struct list *childlist;
++  struct isis_spftree *rdtree;
++
++  if (nicknode == NULL) 
++   {
++     rdtree = area->spftree[TRILL_ISIS_LEVEL-1];
++     if (area->trill->adjnodes)
++        list_delete (area->trill->adjnodes);
++     area->trill->adjnodes = NULL;
++   }
++  else
++   {
++     rdtree = nicknode->rdtree;
++     if (nicknode->adjnodes)
++       list_delete (nicknode->adjnodes);
++     nicknode->adjnodes = NULL;
++   }
++
++  /* Find our node in the distribution tree first */
++  for (ALL_LIST_ELEMENTS_RO (rdtree->paths, node, vertex))
++    {
++      if (vertex->type != VTYPE_NONPSEUDO_IS &&
++	  vertex->type != VTYPE_NONPSEUDO_TE_IS)
++	continue;
++      if (memcmp (vertex->N.id, area->isis->sysid, ISIS_SYS_ID_LEN) == 0)
++        {
++          rbvertex = vertex;
++	  break;
++	}
++    }
++
++  /* Determine adjacencies by looking up the parent & child nodes */
++  if (rbvertex)
++    {
++      adjlist = list_new();
++
++      if (rbvertex->parent)
++        {
++	  /* 
++	   * Find adjacent parent node: check parent is not another vertex 
++	   * with our system ID and the parent node is on SPF paths 
++	   */
++	  vertex = rbvertex->parent;
++	  while (vertex != NULL)
++	    {
++              if (memcmp (vertex->N.id, area->isis->sysid, ISIS_SYS_ID_LEN)
++		  && (listnode_lookup (rdtree->paths, vertex)))
++		break;
++	      vertex = vertex->parent;
++	    }
++	  if (vertex != NULL)
++            trill_add_nickadjlist (area, adjlist, vertex);
++	}
++
++      if (rbvertex->children && listhead (rbvertex->children)) 
++        {
++	   childlist = list_new();
++           for (ALL_LIST_ELEMENTS_RO (rbvertex->children, node, vertex))
++	     {
++               if (memcmp (vertex->N.id, area->isis->sysid, ISIS_SYS_ID_LEN) == 0)
++                 listnode_add(childlist, vertex);
++	       else if (listnode_lookup (rdtree->paths, vertex))
++                 trill_add_nickadjlist (area, adjlist, vertex);
++	     }
++
++	   /* 
++	    * If we find child vertices above with our system ID then we search
++	    * their descendants and any that are found are added as our adjacencies.
++	    */
++	   for (node = listhead(childlist); node != NULL; node = listnextnode(node))
++             {
++               if ((vertex = listgetdata(node)) == NULL)
++	         break;
++
++               for (ALL_LIST_ELEMENTS_RO (vertex->children, cnode, cvertex))
++	         {
++                   if ((memcmp (cvertex->N.id, area->isis->sysid, ISIS_SYS_ID_LEN) == 0) &&
++                           listnode_lookup(childlist, cvertex) == NULL)
++                     listnode_add(childlist, cvertex);
++
++		   if (listnode_lookup(rdtree->paths, cvertex))
++                     trill_add_nickadjlist (area, adjlist, cvertex);
++	         }
++	     }
++	   list_delete(childlist);
++	}
++
++      if (nicknode != NULL)
++        nicknode->adjnodes = adjlist;
++      else
++	area->trill->adjnodes = adjlist;
++    }
++  trill_create_vlanfilterlist(area, nicknode);
++}
++ 
++static nickfwdtblnode_t *
++trill_fwdtbl_lookup (struct isis_area *area, u_int16_t nick)
++{
++  struct listnode *node;
++  nickfwdtblnode_t *fwdnode;
++
++  if (area->trill->fwdtbl == NULL)
++    return NULL;
++
++  for (ALL_LIST_ELEMENTS_RO (area->trill->fwdtbl, node, fwdnode))
++    if (fwdnode->dest_nick == nick)
++      return fwdnode;
++
++  return NULL;
++}
++
++static void
++trill_adjtbl_print (struct vty *vty, struct isis_area *area, nicknode_t *nicknode)
++{
++  struct listnode *node;
++  nickfwdtblnode_t *fwdnode;
++  void *listdata;
++  u_int16_t nick;
++  int idx = 0;
++  u_int8_t *vlans;
++  int vlan_set;
++  int vlan;
++  struct list *adjnodes;
++  struct listnode *vnode = NULL;
++  struct list  *vlans_reachable;
++
++  if (nicknode != NULL)
++    {
++      adjnodes = nicknode->adjnodes;
++      vlans_reachable = nicknode->vlans_reachable;
++    }
++  else
++    {
++      adjnodes = area->trill->adjnodes;
++      vlans_reachable = area->trill->vlans_reachable;
++    }
++
++  if (adjnodes == NULL)
++    return;
++
++  if ((vlans_reachable != NULL) &&
++      listcount(adjnodes) == listcount(vlans_reachable))
++    vnode = listhead (vlans_reachable);
++
++  for (ALL_LIST_ELEMENTS_RO (adjnodes, node, listdata))
++    {
++      nick = (u_int16_t)(u_long)listdata;
++      fwdnode = trill_fwdtbl_lookup (area, nick);
++      if (!fwdnode)
++        continue;
++
++      vty_out (vty, "%-15s   %-5d      %-5s  %-15s%s",
++               print_sys_hostname (nick_to_sysid(area, nick)),
++	       ntohs (nick), fwdnode->interface->name,
++	       snpa_print (fwdnode->adj_snpa), VTY_NEWLINE);
++
++      if (vlans_reachable == NULL || vnode == NULL)
++        continue;
++
++      vty_out (vty, "    VLAN filter list:");
++      vlans = listgetdata (vnode);
++      if (vlans == NULL)
++        {
++          vty_out (vty, "%s", VTY_NEWLINE);
++          continue;
++        }
++
++      EACH_VLAN_SET(vlans, vlan, vlan_set)
++       {
++          idx++;
++          if (idx % 8 == 0)
++              vty_out (vty, "%s            ", VTY_NEWLINE);
++          vty_out (vty, "%d ", vlan);
++       }
++      vnode = listnextnode (vnode);
++      vty_out (vty, "%s", VTY_NEWLINE);
++    }
++  vty_out (vty, "%s", VTY_NEWLINE);
++}
++
++static void
++trill_adjtbl_print_all (struct vty *vty, struct isis_area *area)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++
++  vty_out(vty, "Adjacencies on our RBridge distribution tree:%s", VTY_NEWLINE);
++  trill_adjtbl_print (vty, area, NULL);
++
++  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
++    {
++      vty_out(vty, "Adjacencies on RBridge %s distribution tree:%s",
++	      print_sys_hostname (tnode->info.sysid), VTY_NEWLINE);
++      trill_adjtbl_print (vty, area, tnode);
++    }
++}
++
++static void
++trill_ioctl(int fd, unsigned type, void *data)
++{
++  if (isisd_privs.change (ZPRIVS_RAISE))
++    zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
++
++  if (ioctl(fd, type, data) != 0) {
++      zlog_warn ("trill_ioctl() type:%X failed: %s", type, safe_strerror (errno));
++  }
++
++  if (isisd_privs.change (ZPRIVS_LOWER))
++    zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
++}
++
++static void
++trill_publish_nick(struct isis_area *area, int fd, u_int16_t nick,
++    nickfwdtblnode_t *fwdnode)
++{
++  nicknode_t *nick_node;
++  int adjcount = 0;
++  int dtrootcount = 0;
++  int idx;
++  int size;
++  struct listnode *node;
++  void *listdata;
++  struct list *adjnodes;
++  struct list *dtrootnodes;
++  trill_nickinfo_t *ni;
++  struct list *vlans_reachable;
++
++  /* If this is a forwarding entry (not us), then get node data */
++  if (fwdnode != NULL)
++    {
++      nick_node = trill_nicknode_lookup (area, fwdnode->dest_nick);
++      if (nick_node == NULL)
++	 return;
++      adjnodes = nick_node->adjnodes;
++      dtrootnodes = nick_node->info.dt_roots;
++      vlans_reachable = nick_node->vlans_reachable;
++    }
++  else
++    {
++      adjnodes = area->trill->adjnodes;
++      dtrootnodes = area->trill->dt_roots;
++      vlans_reachable = area->trill->vlans_reachable;
++    }
++
++  if (adjnodes != NULL)
++    adjcount = listcount(adjnodes);
++  if (dtrootnodes != NULL)
++    dtrootcount = listcount(dtrootnodes); 
++
++  size = sizeof(trill_nickinfo_t) + (adjcount * sizeof (u_int16_t)) + 
++	  (dtrootcount * sizeof (u_int16_t)) + 
++	  (adjcount * VLANS_ARRSIZE);
++  ni = (trill_nickinfo_t *)calloc(1, size);
++  ni->tni_adjcount = adjcount;
++  ni->tni_dtrootcount = dtrootcount;
++  ni->tni_nick = nick;
++
++  if (fwdnode != NULL)
++    {
++      memcpy(ni->tni_adjsnpa, fwdnode->adj_snpa,
++	     sizeof(fwdnode->adj_snpa));
++      ni->tni_linkid = fwdnode->interface->ifindex;
++    }
++
++  if (adjcount > 0)
++    {
++      idx = 0;
++      for (ALL_LIST_ELEMENTS_RO (adjnodes, node, listdata))
++        {
++          TNI_ADJNICK(ni, idx) = (u_int16_t)(u_long)listdata;
++	  idx++;
++        }
++    }
++
++  if (dtrootcount > 0)
++    {
++      idx = 0;
++      for (ALL_LIST_ELEMENTS_RO (dtrootnodes, node, listdata))
++        {
++          TNI_DTROOTNICK(ni, idx) = (u_int16_t)(u_long)listdata;
++	  idx++;
++        }
++    }
++
++  if (vlans_reachable != NULL)
++    {
++      idx = 0;
++      for (ALL_LIST_ELEMENTS_RO (vlans_reachable, node, listdata))
++        {
++	  memcpy (TNI_VLANFILTERMAP(ni, idx), listdata, VLANS_ARRSIZE);
++	  idx++;
++        }
++    }
++
++  trill_ioctl (fd, fwdnode == NULL ? TRILL_SETNICK : TRILL_ADDNICK, ni);
++  free(ni);
++}
++
++static void
++trill_publish (struct isis_area *area, struct isis_circuit *trill_circuit)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++  struct listnode *node;
++  nickfwdtblnode_t *fwdnode;
++  u_char *lsysid;
++  u_int16_t lpriority;
++  u_int16_t root_nick;
++
++  trill_ioctl(trill_circuit->fd, TRILL_DELALL, NULL);
++
++  if (area->trill->fwdtbl != NULL)
++    {
++      for (ALL_LIST_ELEMENTS_RO (area->trill->fwdtbl, node, fwdnode))
++        {
++	  trill_publish_nick(area, trill_circuit->fd, fwdnode->dest_nick,
++	    fwdnode);
++        }
++    }
++
++  trill_publish_nick(area, trill_circuit->fd, area->trill->nick.name, NULL);
++
++  /* Compute the highest priority root tree node  */
++  lpriority = area->trill->root_priority;
++  lsysid = area->isis->sysid;
++  root_nick = area->trill->nick.name;
++
++  /*
++   * Highest priority tree root is determined by the numerically lowest
++   * priority field or if priorities are equal then by lowest system ID. 
++   */
++  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
++    {
++      if (tnode->info.root_priority > lpriority)
++        continue;
++      if (tnode->info.root_priority == lpriority &&
++          memcmp(tnode->info.sysid, lsysid, ISIS_SYS_ID_LEN) > 0)
++        continue;
++      lpriority = tnode->info.root_priority;
++      lsysid = tnode->info.sysid;
++      root_nick = tnode->info.nick.name;
++    }
++  trill_ioctl(trill_circuit->fd, TRILL_TREEROOT, &root_nick); 
++}
++
++void
++trill_set_vlan_forwarder (struct isis_circuit *circuit, u_int8_t *forwarder)
++{
++  trill_ioctl(circuit->fd, TRILL_VLANFWDER, forwarder);
++}
++
++void
++trill_port_flush (struct isis_circuit *circuit, u_int16_t vlan)
++{
++  trill_ioctl(circuit->fd, TRILL_PORTFLUSH, (void *)(unsigned long)vlan);
++}
++
++void
++trill_nick_flush (struct isis_circuit *circuit, u_int16_t vlan)
++{
++  trill_ioctl(circuit->fd, TRILL_NICKFLUSH, (void *)(unsigned long)vlan);
++}
++
++/*
++ * Called upon computing the SPF trees to create the forwarding
++ * and adjacency lists for TRILL.
++ */
++void
++trill_process_spf (struct isis_area *area)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++  struct listnode *node;
++  struct isis_circuit *trill_circuit = NULL;
++
++  /* Nothing to do if we don't have a nick yet */
++  if (area->trill->nick.name == RBRIDGE_NICKNAME_NONE)
++	  return;
++
++  if (area->circuit_list && listhead(area->circuit_list))
++    trill_circuit = listgetdata(listhead(area->circuit_list));
++  if (trill_circuit == NULL)
++    return;
++
++  trill_create_nickfwdtable(area);
++  trill_create_nickadjlist(area, NULL);
++  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
++    trill_create_nickadjlist(area, tnode);
++
++  trill_publish(area, trill_circuit);
++
++  for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, trill_circuit))
++    {
++      trill_ioctl(trill_circuit->fd, TRILL_DESIGVLAN,
++	&trill_circuit->vlans->designated);
++      if (trill_circuit->vlans->inhibit_all == 0)
++	trill_set_vlan_forwarder (trill_circuit,
++	    trill_circuit->vlans->forwarder);
++    }
++}
++
++void
++trill_nickdb_print (struct vty *vty, struct isis_area *area)
++{
++  dnode_t *dnode;
++  nicknode_t *tnode;
++  const char *sysid;
++  int vlan_count = 0;
++  int vlan_set;
++  int vlan;
++
++  vty_out(vty, "    System ID          Hostname     Nickname" 
++	 "   Priority%s", VTY_NEWLINE); 
++  for (ALL_DICT_NODES_RO(area->trill->nickdb, dnode, tnode))
++    {
++      sysid = sysid_print (tnode->info.sysid);
++      vty_out (vty, "%-21s %-10s  %8d  %8d%s", sysid,
++               print_sys_hostname (tnode->info.sysid),
++	       ntohs (tnode->info.nick.name),
++	       tnode->info.nick.priority, VTY_NEWLINE);
++
++      vty_out (vty, "    VLAN Forwarder: ");
++      EACH_VLAN_SET(tnode->info.vlans_forwarder, vlan, vlan_set)
++        {
++	   vlan_count++;
++	   if (vlan_count % 8 == 0)
++             vty_out(vty, "%s               ", VTY_NEWLINE);
++           vty_out (vty, "%d ", vlan); 
++        }
++      vty_out (vty, "%s", VTY_NEWLINE); 
++    }
++}
++
++static int
++ethercmp(const void *e1, const void *e2)
++{
++  return memcmp (e1, e2, ETH_ALEN);
++}
++
++static int
++gather_bridge_ids(struct isis_area *area,
++    struct trill_vlan_bridge_roots_subtlv *vlantlv, int vlan)
++{
++  time_t now;
++  int circnt;
++  struct listnode *node;
++  struct isis_circuit *circuit;
++  char *bptr, *obptr;
++  ptrdiff_t numbytes;
++
++  now = time (NULL);
++  circnt = 0;
++  bptr = (char *)(vlantlv + 1);
++  for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
++    {
++      if (CHECK_VLAN(circuit->vlans->forwarder, vlan))
++	{
++	  circnt++;
++	  /*
++	   * Note that it's ok for circuits to lack a root bridge ID.  There
++	   * may just not be a bridge out there.  (Ultimately, in the future,
++	   * that's where we'd like to be.)
++	   */
++	  if (circuit->root_expire != 0 && now - circuit->root_expire <= 0)
++	    {
++	      /* ignore bridge priority; only the MAC ID is wanted */
++	      memcpy (bptr, circuit->root_bridge + 2, ETH_ALEN);
++	      bptr += ETH_ALEN;
++	    }
++	}
++    }
++
++  /* Sort the bridge IDs for ease of comparison, and then remove dups */
++  obptr = (char *)(vlantlv + 1);
++  numbytes = bptr - obptr;
++  if (numbytes > ETH_ALEN)
++    {
++      qsort(obptr, numbytes / ETH_ALEN, ETH_ALEN, ethercmp);
++      while (obptr < bptr - ETH_ALEN)
++	{
++	  if (memcmp (obptr, obptr + ETH_ALEN, ETH_ALEN) == 0)
++	    {
++	      memmove (obptr, obptr + ETH_ALEN, (bptr - obptr) - ETH_ALEN);
++	      bptr -= ETH_ALEN;
++	      numbytes -= ETH_ALEN;
++	    }
++	  else
++	    {
++	      obptr += ETH_ALEN;
++	    }
++	}
++    }
++
++  /* Store the root bridge byte count here for the caller */
++  vlantlv->vlan_end = numbytes;
++  return circnt;
++}
++
++/* 
++ * Add TLVs necessary to advertise TRILL nickname using router capabilities TLV
++ */
++int
++tlv_add_trill_nickname(struct trill_nickname *nick_info,
++  struct stream *stream, struct  isis_area *area)
++{
++  size_t tlvstart;
++  struct router_capability_tlv rtcap;
++  u_char tflags;
++  struct trill_nickname_subtlv tn;
++  int rc;
++  int vlan;
++  int circnt;
++  int nbytes;
++  struct trill_vlan_bridge_roots_subtlv *lastvlantlv, *nextvlantlv;
++
++  tlvstart = stream_get_endp (stream);
++
++  (void) memset(&rtcap, 0, sizeof (rtcap));
++  rc = add_tlv(ROUTER_CAPABILITY, sizeof (rtcap), (uchar_t *)&rtcap, stream);
++  if (rc != ISIS_OK)
++    return rc;
++
++  tflags = TRILL_FLAGS_V0;
++  rc = add_subtlv (RCSTLV_TRILL_FLAGS, sizeof (tflags), (uchar_t *)&tflags,
++      tlvstart, stream);
++  if (rc != ISIS_OK)
++    return rc;
++
++  tn.tn_priority = nick_info->priority;
++  tn.tn_nickname = nick_info->name;
++  tn.tn_trootpri = htons(area->trill->root_priority);
++  tn.tn_treecount = htons(0);
++  rc = add_subtlv (RCSTLV_TRILL_NICKNAME, sizeof (tn), (uchar_t *)&tn, tlvstart,
++      stream);
++  if (rc != ISIS_OK)
++    return rc;
++
++  /*
++   * The algorithm below is designed to find the set of VLANs for which we are
++   * appointed forwarder for at least one circuit, and organize them into lists
++   * (each with a separate sub-TLV) based on root 802.1D bridge ID.  The lists
++   * must be contiguous and must have exactly the same set of root IDs, but
++   * need not have the same set of circuits involved.
++   *
++   * We currently don't support multicast snooping, so the complexities of the
++   * M4/M6/OM bits are spared here.
++   */
++  circnt = listcount(area->circuit_list);
++  if (circnt == 0)
++    return rc;
++
++  /* Aligned: Ethernet addresses are 6 bytes, and the subTLV uses uint16_t */
++  lastvlantlv = XMALLOC (MTYPE_ISIS_TRILL_VLANSUBTLV,
++      2 * (sizeof (*lastvlantlv) + circnt * ETH_ALEN));
++  nextvlantlv = (struct trill_vlan_bridge_roots_subtlv *)
++      ((char *)(lastvlantlv + 1) + circnt * ETH_ALEN);
++
++  vlan = circnt = 0;
++  while (vlan <= VLAN_MAX)
++    {
++      /*
++       * If this is the first VLAN or if the last pass ended on an unused VLAN,
++       * then scan ahead to find the start of the next range that's in use.
++       * Otherwise, copy down the last one found.
++       */
++      if (circnt == 0)
++	{
++	  for (vlan++; vlan <= VLAN_MAX; vlan++)
++	  {
++	    circnt = gather_bridge_ids (area, lastvlantlv, vlan);
++	    if (circnt != 0)
++	      break;
++	  }
++	  if (circnt == 0)
++	    break;
++	}
++      else
++	{
++	  memcpy (lastvlantlv, nextvlantlv,
++	      nextvlantlv->vlan_end + sizeof (*nextvlantlv));
++	}
++
++      /*
++       * Set the multicast bits, because we don't support IGMP/MLD
++       * snooping, and we thus need to see all multicast frames.
++       */
++      lastvlantlv->vlan_start = htons (vlan | TVRFS_M4 |  TVRFS_M6 |  TVRFS_OM);
++      nbytes = lastvlantlv->vlan_end;
++
++      /*
++       * Now locate the end of the compatible set of VLANs: these are the ones
++       * where we're appointed forwarder on at least one circuit, and the list
++       * of root bridge IDs is identical to the current one.
++       */
++      for (vlan++; vlan <= VLAN_MAX; vlan++)
++	{
++	  circnt = gather_bridge_ids (area, nextvlantlv, vlan);
++	  if (circnt == 0 || nbytes != nextvlantlv->vlan_end ||
++	      memcmp (lastvlantlv + 1, nextvlantlv + 1, nbytes != 0))
++	    break;
++	}
++
++      lastvlantlv->vlan_end = htons (vlan - 1);
++
++      /*
++       * Insert the subTLV into the list, starting a new TLV if it won't fit in
++       * the current one.
++       */
++      nbytes += sizeof (*lastvlantlv);
++      rc = add_subtlv (RCSTLV_TRILL_VLANSROOTS, nbytes, (uchar_t *)lastvlantlv,
++	  tlvstart, stream);
++      if (rc == ISIS_WARNING)
++      {
++	tlvstart = stream_get_endp (stream);
++	rc = add_tlv(ROUTER_CAPABILITY, sizeof (rtcap), (uchar_t *)&rtcap,
++	    stream);
++	if (rc != ISIS_OK)
++	  break;
++	rc = add_subtlv (RCSTLV_TRILL_VLANSROOTS, nbytes,
++	    (uchar_t *)lastvlantlv, tlvstart, stream);
++	if (rc != ISIS_OK)
++	  break;
++      }
++    }
++
++  XFREE (MTYPE_ISIS_TRILL_VLANSUBTLV, lastvlantlv);
++
++  return rc;
++}
++
++DEFUN (debug_trill_events,
++       debug_trill_events_cmd,
++       "debug trill events",
++       DEBUG_STR
++       "IS-IS information\n"
++       "IS-IS TRILL Events\n")
++{
++  isis->debugs |= DEBUG_TRILL_EVENTS;
++  print_debug (vty, DEBUG_TRILL_EVENTS, 1);
++
++  return CMD_SUCCESS;
++}
++
++DEFUN (no_debug_trill_events,
++       no_debug_trill_events_cmd,
++       "no debug trill events",
++       UNDEBUG_STR
++       "IS-IS information\n"
++       "IS-IS TRILL Events\n")
++{
++  isis->debugs &= ~DEBUG_TRILL_EVENTS;
++  print_debug (vty, DEBUG_TRILL_EVENTS, 0);
++
++  return CMD_SUCCESS;
++}
++
++DEFUN (show_trill_nickdatabase,
++       show_trill_nickdatabase_cmd,
++       "show trill nickname database",
++       SHOW_STR TRILL_STR "TRILL IS-IS nickname information\n"
++       "IS-IS TRILL nickname database\n")
++{
++  struct listnode *node;
++  struct isis_area *area;
++
++  if (!isis->trill_active || (isis->area_list->count == 0))
++    return CMD_SUCCESS;
++
++  assert (isis->area_list->count == 1);
++
++  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
++    {
++      vty_out (vty, "Area %s nickname:%d priority:%d%s%s",
++          area->area_tag ? area->area_tag : "null", 
++          ntohs(area->trill->nick.name), area->trill->nick.priority,
++          VTY_NEWLINE, VTY_NEWLINE);
++      vty_out (vty, "IS-IS TRILL nickname database:%s", VTY_NEWLINE);
++          trill_nickdb_print (vty, area);
++    }
++
++  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
++  return CMD_SUCCESS;
++}
++
++DEFUN (show_trill_fwdtable,
++       show_trill_fwdtable_cmd,
++       "show trill forwarding",
++       SHOW_STR TRILL_STR
++       "IS-IS TRILL forwarding table\n")
++{
++  struct listnode *node;
++  struct isis_area *area;
++
++  if (!isis->trill_active || (isis->area_list->count == 0))
++    return CMD_SUCCESS;
++
++  assert (isis->area_list->count == 1);
++
++  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
++    {
++      vty_out (vty, "IS-IS TRILL forwarding table:%s", VTY_NEWLINE);
++      trill_fwdtbl_print (vty, area);
++    }
++
++  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
++  return CMD_SUCCESS;
++}
++
++DEFUN (show_trill_circuits,
++       show_trill_circuits_cmd,
++       "show trill circuits",
++       SHOW_STR TRILL_STR
++       "IS-IS TRILL circuits\n")
++{
++  struct listnode *node;
++  struct isis_area *area;
++
++  if (!isis->trill_active || (isis->area_list->count == 0))
++    return CMD_SUCCESS;
++
++  assert (isis->area_list->count == 1);
++
++  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
++    {
++      vty_out (vty, "IS-IS TRILL circuits:%s%s",
++		      VTY_NEWLINE, VTY_NEWLINE);
++      trill_circuits_print_all (vty, area);
++    }
++
++  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
++  return CMD_SUCCESS;
++}
++
++DEFUN (show_trill_adjtable,
++       show_trill_adjtable_cmd,
++       "show trill adjacencies",
++       SHOW_STR TRILL_STR
++       "IS-IS TRILL adjacency lists\n")
++{
++  struct listnode *node;
++  struct isis_area *area;
++
++  if (!isis->trill_active || (isis->area_list->count == 0))
++    return CMD_SUCCESS;
++
++  assert (isis->area_list->count == 1);
++
++  for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
++    {
++      vty_out (vty, "IS-IS TRILL adjacencies in all distribution trees:%s%s",
++		      VTY_NEWLINE, VTY_NEWLINE);
++          trill_adjtbl_print_all (vty, area);
++    }
++
++  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
++  return CMD_SUCCESS;
++}
++
++/*
++ * Enable TRILL support in IS-IS command, only one IS-IS area allowed.
++ */
++DEFUN (isis_trill,
++       isis_trill_cmd,
++       "isis trill",
++       "Enable use of IS-IS as routing protocol for TRILL\n")
++{
++  if (!isis->trill_active && isis->area_list->count > 0)
++    {
++      vty_out (vty, "Cannot enable TRILL. IS-IS area already configured%s",
++		      VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++
++  isis->trill_active = TRUE;
++  return CMD_SUCCESS;
++}
++
++/*
++ * Disable TRILL support in IS-IS command
++ */
++DEFUN (no_isis_trill,
++       no_isis_trill_cmd,
++       "no isis trill",
++       "Disable use of IS-IS as routing protocol for TRILL\n")
++{
++  isis->trill_active = FALSE;
++  return CMD_SUCCESS;
++}
++
++DEFUN (trill_nickname,
++       trill_nickname_cmd,
++       "trill nickname WORD",
++       TRILL_STR
++       TRILL_NICK_STR
++       "<1-65534>\n")
++{
++  struct isis_area *area;
++  u_int16_t nickname;
++
++  area = vty->index;
++  assert (area);
++  assert (area->isis);
++  if (!area->isis->trill_active)
++    {
++      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++
++  VTY_GET_INTEGER_RANGE ("TRILL nickname", nickname, argv[0], 
++		  RBRIDGE_NICKNAME_MIN + 1, RBRIDGE_NICKNAME_MAX);
++  if (!trill_area_nickname (area, nickname))
++    {
++      vty_out (vty, "TRILL nickname conflicts with another RBridge nickname,"
++		    " must select another.%s", VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++  return CMD_SUCCESS;
++}
++
++DEFUN (no_trill_nickname,
++       no_trill_nickname_cmd,
++       "no trill nickname",
++       TRILL_STR
++       TRILL_NICK_STR)
++{
++  struct isis_area *area;
++
++  area = vty->index;
++  assert (area);
++  assert (area->isis);
++  if (!area->isis->trill_active)
++    {
++      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++
++  trill_area_nickname (area, 0);
++  return CMD_SUCCESS;
++}
++
++DEFUN (trill_nickname_priority,
++       trill_nickname_priority_cmd,
++       "trill nickname priority WORD",
++       TRILL_STR
++       TRILL_NICK_STR
++       "priority of use field\n"
++       "<1-127>\n")
++{
++  struct isis_area *area;
++  u_int8_t priority;
++
++  area = vty->index;
++  assert (area);
++  assert (area->isis);
++  if (!area->isis->trill_active)
++    {
++      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++
++  VTY_GET_INTEGER_RANGE ("TRILL nickname priority", priority, argv[0],
++		  MIN_RBRIDGE_PRIORITY, MAX_RBRIDGE_PRIORITY);
++  trill_nickname_priority_update (area, priority);
++  return CMD_SUCCESS;
++}
++
++DEFUN (no_trill_nickname_priority,
++       no_trill_nickname_priority_cmd,
++       "no trill nickname priority WORD",
++       TRILL_STR
++       TRILL_NICK_STR
++       "priority of use field\n")
++{
++  struct isis_area *area;
++
++  area = vty->index;
++  assert (area);
++  assert (area->isis);
++  if (!area->isis->trill_active)
++    {
++      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++
++  trill_nickname_priority_update (area, 0);
++  return CMD_SUCCESS;
++}
++
++DEFUN (trill_instance,
++       trill_instance_cmd,
++       "trill instance WORD",
++       TRILL_STR
++       "TRILL instance\n"
++       "instance name\n")
++{
++  struct isis_area *area;
++
++  area = vty->index;
++  assert (area);
++  assert (area->isis);
++  if (!area->isis->trill_active)
++    {
++      vty_out (vty, "TRILL is not enabled%s", VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++
++  (void) strlcpy(area->trill->name, argv[0], MAXLINKNAMELEN);
++  return CMD_SUCCESS;
++}
++
++void
++install_trill_elements (void)
++{
++  install_element (VIEW_NODE, &show_trill_nickdatabase_cmd);
++  install_element (VIEW_NODE, &show_trill_fwdtable_cmd);
++  install_element (VIEW_NODE, &show_trill_adjtable_cmd);
++  install_element (VIEW_NODE, &show_trill_circuits_cmd);
++
++  install_element (ENABLE_NODE, &debug_trill_events_cmd);
++  install_element (ENABLE_NODE, &no_debug_trill_events_cmd);
++  install_element (ENABLE_NODE, &show_trill_nickdatabase_cmd);
++  install_element (ENABLE_NODE, &show_trill_fwdtable_cmd);
++  install_element (ENABLE_NODE, &show_trill_adjtable_cmd);
++  install_element (ENABLE_NODE, &show_trill_circuits_cmd);
++
++  install_element (CONFIG_NODE, &debug_trill_events_cmd);
++  install_element (CONFIG_NODE, &no_debug_trill_events_cmd);
++  install_element (CONFIG_NODE, &isis_trill_cmd);
++  install_element (CONFIG_NODE, &no_isis_trill_cmd);
++
++  install_element (ISIS_NODE, &trill_nickname_cmd);
++  install_element (ISIS_NODE, &no_trill_nickname_cmd);
++  install_element (ISIS_NODE, &trill_nickname_priority_cmd);
++  install_element (ISIS_NODE, &no_trill_nickname_priority_cmd);
++  install_element (ISIS_NODE, &trill_instance_cmd);
++
++  install_trill_vlan_elements ();
++}
++
++static int
++update_link(dladm_handle_t handle, datalink_id_t linkid, void *arg)
++{
++  struct isis_area *area = arg;
++  dladm_status_t status;
++  char bridge[MAXLINKNAMELEN], linkname[MAXLINKNAMELEN];
++  char pointless[DLADM_STRSIZE];
++  datalink_class_t class;
++  struct interface *ifp;
++  struct isis_circuit *circ;
++  uint_t propval, valcnt;
++
++  status = dladm_bridge_getlink (handle, linkid, bridge, sizeof (bridge));
++  if (status != DLADM_STATUS_OK || strcmp (bridge, area->trill->name) != 0)
++    return DLADM_WALK_CONTINUE;
++
++  status = dladm_datalink_id2info (handle, linkid, NULL, &class, NULL,
++      linkname, sizeof (linkname));
++  if (status == DLADM_STATUS_OK)
++    {
++      ifp = if_get_by_name (linkname);
++      ifp->ifindex = linkid;
++      ifp->flags |= IFF_UP | IFF_RUNNING;
++
++      /*
++       * This value is arbitrary.  The real interface MTU will be read out of
++       * the kernel when isis_circuit_up calls the TRILL socket interface.
++       */
++      if (ifp->mtu == 0)
++	ifp->mtu = 1500;
++      *(datalink_id_t *)ifp->sdl.sdl_data = linkid;
++      ifp->sdl.sdl_nlen = sizeof (datalink_id_t);
++      if ((circ = ifp->info) == NULL)
++	{
++	  circ = isis_csm_state_change (IF_UP_FROM_Z, NULL, ifp);
++	  circ = isis_csm_state_change (ISIS_ENABLE, circ, area);
++	}
++      /*
++       * The second state change has caused us to open up the socket for this
++       * link and read the Ethernet address.  Copy that into place for the
++       * interface structure.
++       */
++      ifp->sdl.sdl_alen = ETH_ALEN;
++      memcpy (LLADDR (&ifp->sdl), circ->u.bc.snpa, ETH_ALEN);
++      valcnt = 1;
++      status = dladm_get_linkprop_values (dlhandle, linkid,
++	  DLADM_PROP_VAL_PERSISTENT, "default_tag", &propval, &valcnt);
++      if (status == DLADM_STATUS_OK)
++	circ->vlans->pvid = propval;
++      memset (circ->vlans->enabled, 0, VLANS_ARRSIZE);
++      if (circ->vlans->pvid != 0)
++	SET_VLAN (circ->vlans->enabled, circ->vlans->pvid);
++    }
++  else
++    {
++      zlog_err ("unable to get link info for ID %u: %s", linkid,
++	  dladm_status2str (status, pointless));
++    }
++  return DLADM_WALK_CONTINUE;
++}
++
++static int
++set_vlan(dladm_handle_t handle, datalink_id_t linkid, void *arg)
++{
++  dladm_status_t status;
++  dladm_vlan_attr_t vinfo;
++  char pointless[DLADM_STRSIZE];
++  struct interface *ifp;
++  struct isis_circuit *circuit;
++
++  status = dladm_vlan_info (handle, linkid, &vinfo, DLADM_OPT_ACTIVE);
++  if (status != DLADM_STATUS_OK)
++  {
++    zlog_debug ("can't get VLAN info on link ID %u: %s",
++	linkid, dladm_status2str (status, pointless));
++    return DLADM_WALK_CONTINUE;
++  }
++
++  ifp = if_lookup_by_index (vinfo.dv_linkid);
++  if (ifp != NULL)
++  {
++    circuit = ifp->info;
++    SET_VLAN (circuit->vlans->enabled, vinfo.dv_vid);
++  }
++  return DLADM_WALK_CONTINUE;
++}
++
++static char
++trill_internal_reload(struct isis_area *area)
++{
++  struct interface *ifp;
++  struct listnode *node, *nnode;
++  struct isis_circuit *circ;
++  dladm_status_t status;
++  char errmsg[DLADM_STRSIZE];
++
++  if ((status = dladm_open (&dlhandle)) != DLADM_STATUS_OK)
++    {
++      zlog_err ("%s: unable to open datalink control: %s",
++          area->trill->name, dladm_status2str(status, errmsg));
++      return FALSE;
++    }
++
++  /*
++   * Turn off the IFF_UP bit for every link.  Any links left over at the end
++   * without that flag must have been removed.
++   */
++  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
++    ifp->flags &= ~IFF_UP;
++
++  /* Get all of the links configured on this bridge */
++  dladm_walk_datalink_id (update_link, dlhandle, area, DATALINK_CLASS_ALL,
++      DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
++
++  /* Disable ones that have been removed */
++  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
++    {
++      if (!(ifp->flags & IFF_UP) && (circ = ifp->info) != NULL)
++	{
++	  isis_csm_state_change (ISIS_DISABLE, circ, area);
++	  isis_csm_state_change (IF_DOWN_FROM_Z, circ, area);
++	}
++    }
++
++  /* Now get the VLANs */
++  dladm_walk_datalink_id (set_vlan, dlhandle, area, DATALINK_CLASS_VLAN,
++      DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
++
++  dladm_close (dlhandle);
++  return TRUE;
++}
++
++/*
++ * This is run synchronously by the interrupt handling logic when SIGHUP
++ * occurs.  We use this to signal a "refresh" from SMF.  If the user has
++ * specified an explicit configuration file, or if the update fails, then we
++ * just fall through to the normal reload (by way of exec) mechanism.
++ */
++char
++trill_reload(void)
++{
++  if (cfile_present)
++    return FALSE;
++  else
++    return trill_internal_reload (listgetdata (listhead (isis->area_list)));
++}
++
++/*
++ * This function runs before the regular configuration file (if any) is read,
++ * and simulates a configuration read by setting up internal information based
++ * on data stored in dladm.  The user may override this configuration (for
++ * debugging purposes) by specifying a configuration file on the command line.
++ * Otherwise, we force the caller to read /dev/null.
++ */
++void
++trill_read_config (char **cfilep, int argc, char **argv)
++{
++  const char *instname;
++  u_int16_t nickname;
++  struct isis_area *area;
++  struct listnode *ifnode;
++  struct interface *ifp;
++  struct area_addr *addr;
++
++  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, LOG_WARNING);
++
++  if (optind != argc - 1)
++  {
++    zlog_err ("instance name is required for TRILL");
++    exit (1);
++  }
++  instname = argv[optind];
++
++  isis->trill_active = TRUE;
++  area = isis_area_create (instname);
++  (void) strlcpy (area->trill->name, instname, MAXLINKNAMELEN);
++
++  /* Set up to use new (extended) metrics only. */
++  area->newmetric = 1;
++  area->oldmetric = 0;
++
++  /* IS-IS for TRILL is different from the standard; it uses one area address */
++  isis->max_area_addrs = 1;
++
++  if (!trill_internal_reload (area))
++    exit(1);
++
++  /* Recover a previous nickname, if any. */
++  nickname = dladm_bridge_get_nick(instname);
++  if (nickname != RBRIDGE_NICKNAME_NONE && is_nickname_used (nickname))
++  {
++    zlog_warn ("%s: unable to use previous nickname %u", instname, nickname);
++    nickname = RBRIDGE_NICKNAME_NONE;
++  }
++  if (nickname != RBRIDGE_NICKNAME_NONE)
++  {
++    area->trill->nick.name = htons (nickname);
++    SET_FLAG (area->trill->status, TRILL_NICK_SET);
++    SET_FLAG (area->trill->status, TRILL_AUTONICK);
++  }
++
++  /* Set up the area and system ID */
++  ifnode = listhead (iflist);
++  if (ifnode != NULL)
++    {
++      ifp = listgetdata (ifnode);
++      addr = XMALLOC (MTYPE_ISIS_AREA_ADDR, sizeof (struct area_addr));
++      addr->addr_len = 8;
++      addr->area_addr[0] = 0;
++      memcpy (addr->area_addr + 1, LLADDR (&ifp->sdl), ifp->sdl.sdl_alen);
++      addr->area_addr[7] = 0;
++      memcpy (isis->sysid, GETSYSID (addr, ISIS_SYS_ID_LEN), ISIS_SYS_ID_LEN);
++      isis->sysid_set = 1;
++      /* Forget the systemID part of the address */
++      addr->addr_len -= (ISIS_SYS_ID_LEN + 1);
++      listnode_add (area->area_addrs, addr);
++      lsp_l1_generate (area);
++      lsp_l2_generate (area);
++    }
++
++  if (*cfilep == NULL)
++    {
++      *(const char **)cfilep = "/dev/null";
++      cfile_present = FALSE;
++    }
++}
+diff --git isisd/isis_trill.h isisd/isis_trill.h
+new file mode 100644
+index 0000000..88b9f83
+--- /dev/null
++++ isisd/isis_trill.h
+@@ -0,0 +1,148 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_trill.h
++ *
++ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#ifndef _ZEBRA_ISIS_TRILL_H
++#define _ZEBRA_ISIS_TRILL_H
++
++#define	ISO_BPDU	0x42
++
++/* IETF TRILL protocol defined constants */
++#define DFLT_NICK_PRIORITY 0x40			/* Default priority for autogen nicks */
++#define CONFIGURED_NICK_PRIORITY 0x80		/* MSB of priority set if nick is configured */
++#define MIN_RBRIDGE_PRIORITY 1			/* Min priority of use value */
++#define MAX_RBRIDGE_PRIORITY 127		/* Max priority of use value */
++#define MAX_RBRIDGE_NODES (RBRIDGE_NICKNAME_MAX + 1) /* Max RBridges possible */
++#define TRILL_NICKNAME_LEN   2			/* 16-bit nickname */
++#define TRILL_DFLT_ROOT_PRIORITY 0x8000		/* Default tree root priority */
++
++/* Constants used in nickname generation/allocation */
++#define NICKNAMES_BITARRAY_SIZE (MAX_RBRIDGE_NODES / 8) /* nick usage array */
++#define CLEAR_BITARRAY_ENTRYLEN 4	   /* stores nicks available per 32 nicks in nick bitarray */
++#define CLEAR_BITARRAY_ENTRYLENBITS (4*8)  /* 32 nicks tracked in each entry */
++#define CLEAR_BITARRAY_SIZE (MAX_RBRIDGE_NODES / CLEAR_BITARRAY_ENTRYLENBITS)
++
++/* Constants used to track LSP DB acquisition */
++#define MIN_LSPDB_ACQTRIES 2	/* min two LSP PSNP/CSNP send/recv for LSP DB acquisition */
++#define MAX_LSPDB_ACQTRIES 6	/* max LSP PSNP/CSNP send/recv for LSP DB acquisition on any circuit */
++
++/* Macros used to track LSP DB acquisition */
++#define LSPDB_ACQTRYINC(F, C) ((F)->trill->lspdb_acq_reqs[(C)])++
++#define LSPDB_ACQTRYVAL(F, C) ((F)->trill->lspdb_acq_reqs[(C)])
++
++/* trill_info status flags */
++#define TRILL_AUTONICK       (1 << 0)  /* nickname auto-generated (else user-provided) */
++#define TRILL_LSPDB_ACQUIRED (1 << 1)  /* LSP DB acquired before autogen nick is advertised */
++#define TRILL_NICK_SET       (1 << 2)  /* nickname configured (random/user generated) */
++#define TRILL_PRIORITY_SET   (1 << 3)  /* nickname priority configured by user */
++
++/* TRILL information (area-specific) */
++struct trill_info
++{
++  struct trill_nickname nick;   /* our nick */
++  int status; 			/* status flags */
++  dict_t *nickdb;	  	/* TRILL nickname database */
++  dict_t *sysidtonickdb;  	/* TRILL sysid-to-nickname database */
++  /* counter used in LSP database acquisition (per circuit) */
++  u_int8_t lspdb_acq_reqs [ISIS_MAX_CIRCUITS_COUNT];
++  struct list *fwdtbl;          /* RBridge forwarding table */
++  struct list *adjnodes;	/* Adjacent nicks for our distrib. tree */
++  struct list *dt_roots;	/* Our choice of DT roots */
++  struct list *vlans_reachable; /* Per adj and per tree vlans reachable downstream list */
++  u_int16_t root_priority;      /* Root tree priority */
++  char name[MAXLINKNAMELEN];	/* instance name */
++};
++
++/* TRILL nickname information (node-specific) */
++typedef struct nickinfo
++{
++  struct trill_nickname nick;       /* Nick of the node  */
++  u_char sysid[ISIS_SYS_ID_LEN];    /* NET/sysid of node */
++  u_int8_t flags;                   /* TRILL flags advertised by node */
++  struct list *dt_roots;            /* Distrib. Trees chosen by node */
++  u_int16_t root_priority;          /* Root tree priority */
++  u_int16_t root_count;		    /* Root tree count */
++  struct list *broots;		    /* VLANs and Bridge roots */
++  u_int8_t vlans_forwarder[VLANS_ARRSIZE];
++} nickinfo_t;
++
++/* Nickname database node */
++typedef struct trill_nickdb_node
++{
++  nickinfo_t info;		/* Nick info of the node */
++  struct isis_spftree *rdtree;  /* RBridge distribution tree with this nick as root */
++  struct list *adjnodes;	/* Our (host RBridge) adjacent nicks on this distrib. tree */
++  struct list *vlans_reachable; /* Per adj and per tree vlans reachable downstream list */
++  u_int32_t refcnt;		/* ref count */
++} nicknode_t;
++
++/* RBridge search function return status codes */
++typedef enum
++{
++  NOTFOUND = 1,
++  FOUND,
++  DUPLICATE,
++  NICK_CHANGED,
++  PRIORITY_CHANGE_ONLY
++} nickdb_search_result;
++
++/* LSP database acquisition process states */
++typedef enum
++{
++  CSNPRCV = 0,
++  CSNPSND,
++  PSNPSNDTRY,
++} lspdbacq_state;
++
++/* RBridge forwarding table node (1 table per area) */
++typedef struct nickfwdtable_node
++{
++  u_int16_t dest_nick;               /* destination RBridge nick */
++  u_char adj_snpa[ETH_ALEN];         /* MAC address of the adj node */
++  struct interface *interface;       /* if to reach the adj/neigh */
++} nickfwdtblnode_t;
++
++void trill_read_config (char **, int, char **);
++void trill_area_init(struct isis_area *);
++void trill_area_free(struct isis_area *);
++void trill_get_area_nickinfo(struct isis_area *, struct trill_nickname *);
++void trill_nickdb_print (struct vty *, struct isis_area *);
++void trill_nick_destroy(struct isis_lsp *);
++void trill_lspdb_acquire_event(struct isis_circuit *, lspdbacq_state);
++int trill_area_nickname(struct isis_area *, u_int16_t);
++void trill_parse_router_capability_tlvs (struct isis_area *, struct isis_lsp *);
++void trill_process_spf (struct isis_area *);
++void trill_process_hello(struct isis_adjacency *, struct list *);
++void send_trill_vlan_hellos(struct isis_circuit *);
++void trill_circuits_print_all (struct vty *, struct isis_area *);
++u_char *nick_to_sysid(struct isis_area *, u_int16_t); 
++u_int16_t sysid_to_nick(struct isis_area *, u_char *);
++void trill_create_vlanfilterlist(struct isis_area *, nicknode_t *);
++nicknode_t * trill_nicknode_lookup(struct isis_area *, uint16_t);
++void install_trill_elements (void);
++void install_trill_vlan_elements (void);
++int trill_process_bpdu (struct isis_circuit *, u_char *);
++int trill_send_bpdu (struct isis_circuit *circuit, const void *, size_t);
++void trill_send_tc_bpdus (struct isis_circuit *);
++void trill_set_vlan_forwarder (struct isis_circuit *, u_int8_t *);
++void trill_port_flush (struct isis_circuit *, u_int16_t);
++void trill_nick_flush (struct isis_circuit *, u_int16_t);
++void trill_inhib_all(struct isis_circuit *);
++char trill_reload(void);
++#endif
+diff --git isisd/isis_trillbpdu.c isisd/isis_trillbpdu.c
+new file mode 100644
+index 0000000..2352cfc
+--- /dev/null
++++ isisd/isis_trillbpdu.c
+@@ -0,0 +1,202 @@
++/*
++ * TRILL BPDU handling - isis_trillbpdu.c
++ *
++ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++#include <zebra.h>
++#include <time.h>
++#include "log.h"
++#include "if.h"
++#include "stream.h"
++#include "vty.h"
++#include "dict.h"
++#include "isis_common.h"
++#include "isis_constants.h"
++#include "isis_circuit.h"
++#include "isis_tlv.h"
++#include "isis_flags.h"
++#include "isis_vlans.h"
++#include "isis_lsp.h"
++#include "isis_trill.h"
++#include "isisd.h"
++
++/*
++ * This module supports just the bare minimum of Bridge PDU handling necessary
++ * for normal TRILL interaction with standard bridges.  It does not include
++ * spanning tree or other BPDU functions.
++ */
++
++struct common_bpdu
++{
++  u_int16_t cmb_protid;		/* Protocol Identifier */
++  u_int8_t cmb_protvers;	/* Protocol Version Identifier */
++  u_int8_t cmb_type;		/* BPDU Type */
++};
++
++#ifdef __SUNPRO_C
++#pragma pack(1)
++#endif
++
++struct conf_bpdu
++{
++  struct common_bpdu cb_cmb;
++  u_int8_t cb_flags;		/* BPDU Flags */
++  u_int8_t cb_rootid[8];	/* Root Identifier */
++  u_int8_t cb_unused[14];	/* Root Path Cost, Bridge ID, Port ID */
++  u_int16_t cb_messageage;	/* Message Age */
++  u_int16_t cb_maxage;		/* Max Age */
++  u_int16_t cb_hello;		/* Hello Time */
++  u_int16_t cb_unused2;		/* Forward Delay */
++} __attribute__ ((packed));
++
++#ifdef __SUNPRO_C
++#pragma pack()
++#endif
++
++#define	BPDU_PROTID		0	/* Standard STP and RSTP */
++#define	BPDU_PROTVERS_STP	0	/* STP */
++#define	BPDU_PROTVERS_RSTP	2	/* RSTP */
++#define	BPDU_FLAGS_TC_ACK	0x80
++#define	BPDU_FLAGS_TC		1
++#define	BPDU_TYPE_CONF		0
++#define	BPDU_TYPE_RCONF		2
++#define	BPDU_TYPE_TCNOTIF	0x80
++
++int
++trill_process_bpdu (struct isis_circuit *circuit, u_char *srcaddr)
++{
++  size_t bpdulen;
++  struct conf_bpdu cb;
++  time_t now;
++  int brcmp;
++
++  /*
++   * Standard BPDU validation first.  Unrecognized things are just returned
++   * silently.  Bad things (protocol violations) generate warnings.
++   */
++  bpdulen = stream_get_endp (circuit->rcv_stream);
++  if (bpdulen < sizeof (cb.cb_cmb))
++    return ISIS_WARNING;
++
++  stream_get (&cb.cb_cmb, circuit->rcv_stream, sizeof (cb.cb_cmb));
++  if (ntohs(cb.cb_cmb.cmb_protid) != BPDU_PROTID)
++    return ISIS_OK;
++
++  switch (cb.cb_cmb.cmb_type)
++  {
++    case BPDU_TYPE_CONF:
++      if (bpdulen < sizeof (cb))
++	return ISIS_WARNING;
++      stream_get (&cb.cb_cmb + 1, circuit->rcv_stream,
++	  sizeof (cb) - sizeof (cb.cb_cmb));
++      if (ntohs(cb.cb_messageage) >= ntohs(cb.cb_maxage))
++	return ISIS_WARNING;
++      /*
++       * We don't send Configuration BPDUs, so no need to check Bridge & Port
++       * ID values.
++       */
++      break;
++    case BPDU_TYPE_RCONF:
++      if (bpdulen < sizeof (cb) + 1)
++	return ISIS_WARNING;
++      stream_get (&cb.cb_cmb + 1, circuit->rcv_stream,
++	  sizeof (cb) - sizeof (cb.cb_cmb));
++      break;
++    case BPDU_TYPE_TCNOTIF:
++      return ISIS_OK;
++    default:
++      return ISIS_WARNING;
++  }
++
++  brcmp = memcmp (cb.cb_rootid, circuit->root_bridge, sizeof (cb.cb_rootid));
++  now = time (NULL);
++  if (circuit->root_expire == 0 || now - circuit->root_expire > 0 || brcmp <= 0)
++    {
++      int hellot;
++
++      hellot = ntohs(cb.cb_hello) / 256;
++      if (hellot < 1)
++	hellot = 1;
++      else if (hellot > 10)
++	hellot = 10;
++      circuit->root_expire = now + 3 * hellot;
++      memcpy(circuit->root_bridge, cb.cb_rootid, sizeof (cb.cb_rootid));
++
++      /* If root bridge change, then inhibit for a while */
++      if (brcmp != 0)
++	trill_inhib_all (circuit);
++
++     /*
++      * If we've gotten a Topology Change Ack from the root bridge, then we
++      * need not send any more TC notifications.
++      */
++      if ((cb.cb_flags & BPDU_FLAGS_TC) && circuit->tc_count != 0)
++	{
++	  thread_cancel (circuit->tc_thread);
++	  circuit->tc_thread = NULL;
++	  circuit->tc_count = 0;
++	}
++    }
++
++  return ISIS_OK;
++}
++
++/*
++ * Handle TC notification expiry: send another TC BPDU, up to a hard-coded
++ * limit.
++ */
++static int
++trill_send_tc (struct thread *thread)
++{
++  struct isis_circuit *circuit;
++  struct common_bpdu cmb;
++  int retv;
++
++  circuit = THREAD_ARG (thread);
++
++  cmb.cmb_protid = htons (BPDU_PROTID);
++  cmb.cmb_protvers = BPDU_PROTVERS_STP;
++  cmb.cmb_type = BPDU_TYPE_TCNOTIF;
++
++  retv = trill_send_bpdu (circuit, &cmb, sizeof (cmb));
++  if (retv != ISIS_OK)
++    zlog_warn ("TRILL unable to send TC BPDU on %s", circuit->interface->name);
++
++  if (++circuit->tc_count <= 5)
++    {
++      circuit->tc_thread = thread_add_timer (master, trill_send_tc, circuit, 1);
++    }
++  else
++    {
++      circuit->tc_thread = NULL;
++      circuit->tc_count = 0;
++    }
++
++  return retv;
++}
++
++/*
++ * Begin sending TC notification BPDUs on this circuit.  Transmissions are sent
++ * once a second until either 5 have been sent, or we receive a TC Ack from the
++ * root bridge.
++ */
++void
++trill_send_tc_bpdus (struct isis_circuit *circuit)
++{
++  circuit->tc_count = 1;
++  THREAD_TIMER_ON (master, circuit->tc_thread, trill_send_tc, circuit, 1);
++}
+diff --git isisd/isis_trilldummy.c isisd/isis_trilldummy.c
+new file mode 100644
+index 0000000..edaeec3
+--- /dev/null
++++ isisd/isis_trilldummy.c
+@@ -0,0 +1,54 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_trilldummy.c
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#include <zebra.h>
++#include <vty.h>
++#include <if.h>
++
++#include "dict.h"
++#include "bool.h"
++#include "isis_constants.h"
++#include "isis_common.h"
++#include "isis_flags.h"
++#include "isisd.h"
++#include "isis_adjacency.h"
++#include "isis_circuit.h"
++#include "isis_tlv.h"
++#include "isis_lsp.h"
++#include "isis_vlans.h"
++#include "isis_trill.h"
++
++void trill_read_config (char **cfilep, int argc, char **argv) { }
++void trill_process_hello(struct isis_adjacency *adj, struct list *portcaps) { }
++void trill_nickdb_print (struct vty *vty, struct isis_area *area) { }
++void trill_lspdb_acquire_event(struct isis_circuit *circuit,
++    lspdbacq_state caller) { }
++void trill_nick_destroy(struct isis_lsp *lsp) { }
++void send_trill_vlan_hellos(struct isis_circuit *circuit) { }
++void trill_area_init(struct isis_area *area) { }
++void trill_area_free(struct isis_area *area) { }
++void trill_parse_router_capability_tlvs (struct isis_area *area,
++    struct isis_lsp *lsp) { }
++void trill_process_spf (struct isis_area *area) { }
++int tlv_add_trill_nickname(struct trill_nickname *nick_info,
++    struct stream *stream, struct isis_area *area) { return ISIS_OK; }
++int tlv_add_trill_vlans(struct isis_circuit *circuit) { return ISIS_OK; }
++void install_trill_elements (void) { }
++void install_trill_vlan_elements (void) { }
++int trill_process_bpdu (struct isis_circuit *c, u_char *sa) { return ISIS_OK; }
++char trill_reload(void) { return FALSE; }
+diff --git isisd/isis_trillio.c isisd/isis_trillio.c
+new file mode 100644
+index 0000000..858ba1e
+--- /dev/null
++++ isisd/isis_trillio.c
+@@ -0,0 +1,301 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_trillio.c
++ *
++ * Copyright (C) 2001,2002    Sampo Saaristo
++ *                            Tampere University of Technology      
++ *                            Institute of Communications Engineering
++ *
++ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#include <zebra.h>
++#include <net/if_dl.h>
++#include <sys/socket.h>
++#include <sys/stropts.h>
++#include <sys/ethernet.h>
++#include <net/trill.h>
++#include <net/bridge.h>
++
++#include "log.h"
++#include "stream.h"
++#include "network.h"
++#include "if.h"
++#include "vty.h"
++
++#include "isisd/dict.h"
++#include "isisd/include-netbsd/iso.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_common.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_flags.h"
++#include "isisd/isisd.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_network.h"
++#include "isisd/isis_tlv.h"
++#include "isisd/isis_lsp.h"
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
++
++#include "privs.h"
++
++extern struct zebra_privs_t isisd_privs;
++
++static u_char sock_buff[32000];
++
++static const uint8_t all_isis_rbridges[] = ALL_ISIS_RBRIDGES;
++static const uint8_t bridge_group_address[] = BRIDGE_GROUP_ADDRESS;
++
++static int
++open_trill_socket (struct isis_circuit *circuit)
++{
++  struct sockaddr_dl laddr;
++  int fd;
++  unsigned int mtu;
++
++  circuit->fd = -1;
++
++  fd = socket (PF_TRILL, SOCK_DGRAM, 0);
++  if (fd < 0)
++    {
++      zlog_warn ("open_trill_socket(): socket() failed %s",
++		 safe_strerror (errno));
++      return ISIS_ERROR;
++    }
++
++  if (set_nonblocking (fd) < 0)
++    {
++      zlog_warn ("open_trill_socket(): set_nonblocking() failed: %s",
++	  safe_strerror (errno));
++      close (fd);
++      return ISIS_ERROR;
++    }
++
++  if (ioctl (fd, TRILL_NEWBRIDGE, &circuit->area->trill->name) < 0)
++    {
++      zlog_warn ("open_trill_socket(): TRILL_NEWBRIDGE ioctl failed: %s",
++	  safe_strerror (errno));
++      close (fd);
++      return ISIS_ERROR;
++    }
++
++  /*
++   * Bind to the physical interface that must be one of the 
++   * links in the bridge instance.
++   */
++  memset (&laddr, 0, sizeof (struct sockaddr_dl));
++  laddr.sdl_family = AF_TRILL;
++  laddr.sdl_nlen = sizeof (datalink_id_t);
++  *(datalink_id_t *)laddr.sdl_data = circuit->interface->ifindex;
++
++  if (bind (fd, (struct sockaddr *) (&laddr), sizeof (struct sockaddr_dl)) < 0)
++    {
++      zlog_warn ("open_trill_socket(): bind() failed: %s",
++	  safe_strerror (errno));
++      close (fd);
++      return ISIS_ERROR;
++    }
++
++  if (ioctl (fd, TRILL_HWADDR, &circuit->u.bc.snpa) < 0)
++    {
++      zlog_warn ("open_trill_socket(): TRILL_HWADDR ioctl failed: %s",
++	  safe_strerror (errno));
++      close (fd);
++      return ISIS_ERROR;
++    }
++
++  if (ioctl (fd, TRILL_GETMTU, &mtu) < 0)
++    zlog_warn ("open_trill_socket(): TRILL_GETMTU ioctl failed: %s",
++	safe_strerror (errno));
++  else
++    circuit->interface->mtu = mtu;
++
++  if (mtu > sizeof (sock_buff))
++    zlog_err ("open_trill_socket(): interface mtu:%d is greater than "
++        " sock_buff size:%d", mtu, sizeof (sock_buff));
++
++  circuit->fd = fd;
++
++  return ISIS_OK;
++}
++
++/*
++ * Create the socket and set the tx/rx funcs
++ */
++int
++isis_sock_init (struct isis_circuit *circuit)
++{
++  int retval;
++
++  if (isisd_privs.change (ZPRIVS_RAISE))
++    zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
++
++  circuit->tx = isis_send_pdu_bcast;
++  circuit->rx = isis_recv_pdu_bcast;
++
++  retval = open_trill_socket (circuit);
++
++  if (retval != ISIS_OK)
++    {
++      zlog_warn ("%s: could not initialize the socket", __func__);
++      goto end;
++    }
++
++  if (circuit->circ_type == CIRCUIT_T_P2P)
++    {
++      retval = ISIS_ERROR;
++      zlog_err ("%s: do not support P2P link ", __func__);
++    }
++  else if (circuit->circ_type != CIRCUIT_T_BROADCAST)
++    {
++      zlog_warn ("%s: unknown circuit type", __func__);
++      retval = ISIS_WARNING;
++    }
++
++end:
++  if (isisd_privs.change (ZPRIVS_LOWER))
++    zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
++
++  return retval;
++}
++
++int
++isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
++{
++  int bytesread, addr_len;
++  struct sockaddr_dl laddr;
++  char *llsaddr;
++  uint16_t tci;
++  uint8_t sap;
++
++  if (circuit->fd == -1)
++    return ISIS_ERROR;
++
++  /* we have to read to the static buff first */
++  addr_len = sizeof (struct sockaddr_dl);
++  bytesread = recvfrom (circuit->fd, sock_buff, sizeof (sock_buff),
++			MSG_DONTWAIT, (struct sockaddr *) &laddr,
++			(socklen_t *) &addr_len);
++
++  if (bytesread < 0 && errno == EWOULDBLOCK)
++    return ISIS_WARNING;
++
++  if (laddr.sdl_slen != sizeof (tci) || laddr.sdl_alen != ETHERADDRL)
++    return ISIS_ERROR;
++
++  if (bytesread < LLC_LEN)
++    return ISIS_WARNING;
++
++  llsaddr = LLADDR(&laddr);
++  memcpy (ssnpa, llsaddr, laddr.sdl_alen);
++  tci = *(uint16_t *)(llsaddr + laddr.sdl_alen);
++
++  sap = tci == TRILL_TCI_BPDU ? ISO_BPDU : ISO_SAP;
++
++  if (sock_buff[0] != sap || sock_buff[1] != sap || sock_buff[2] != 0x03)
++    return ISIS_WARNING;
++
++  circuit->vlans->rx_tci = tci;
++  stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN);
++
++  return ISIS_OK;
++}
++
++int
++isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
++{
++  ssize_t written;
++  size_t msglen;
++  struct sockaddr_dl laddr;
++  char *dp;
++
++  if (circuit->fd == -1)
++    return ISIS_ERROR;
++
++  stream_set_getp (circuit->snd_stream, 0);
++
++  laddr.sdl_family = AF_TRILL;
++  dp = laddr.sdl_data;
++
++  laddr.sdl_nlen = sizeof (datalink_id_t);
++  memcpy (dp, &circuit->interface->ifindex, sizeof (datalink_id_t));
++  dp += laddr.sdl_nlen;
++
++  laddr.sdl_alen = ETHERADDRL;
++  memcpy (dp, all_isis_rbridges, laddr.sdl_alen);
++  dp += laddr.sdl_alen;
++
++  laddr.sdl_slen = sizeof (circuit->vlans->tx_tci);
++  memcpy (dp, &circuit->vlans->tx_tci, laddr.sdl_slen);
++
++  /* now set up the data in the buffer */
++  sock_buff[0] = ISO_SAP;
++  sock_buff[1] = ISO_SAP;
++  sock_buff[2] = 0x03;
++  msglen = stream_get_endp (circuit->snd_stream);
++  if (msglen + LLC_LEN > sizeof (sock_buff))
++    return ISIS_WARNING;
++  stream_get (sock_buff + LLC_LEN, circuit->snd_stream, msglen);
++  msglen += LLC_LEN;
++
++  /* now we can send this */
++  written = sendto (circuit->fd, sock_buff, msglen, 0,
++		    (struct sockaddr *) &laddr, sizeof (struct sockaddr_dl));
++
++  if (written != (ssize_t)msglen)
++    return ISIS_WARNING;
++
++  return ISIS_OK;
++}
++
++int
++trill_send_bpdu (struct isis_circuit *circuit, const void *msg, size_t msglen)
++{
++  ssize_t written;
++  struct sockaddr_dl laddr;
++  char *dp;
++
++  if (circuit->fd == -1)
++    return ISIS_ERROR;
++
++  /* add in the LLC header */
++  sock_buff[0] = ISO_BPDU;
++  sock_buff[1] = ISO_BPDU;
++  sock_buff[2] = 0x03;
++  memcpy (sock_buff + 3, msg, msglen);
++  msglen += 3;
++
++  laddr.sdl_family = AF_TRILL;
++  dp = laddr.sdl_data;
++
++  laddr.sdl_nlen = sizeof (datalink_id_t);
++  memcpy (dp, &circuit->interface->ifindex, sizeof (datalink_id_t));
++  dp += laddr.sdl_nlen;
++
++  laddr.sdl_alen = ETHERADDRL;
++  memcpy (dp, bridge_group_address, laddr.sdl_alen);
++
++  laddr.sdl_slen = 0;
++
++  written = sendto (circuit->fd, sock_buff, msglen, 0,
++		    (struct sockaddr *) &laddr, sizeof (struct sockaddr_dl));
++
++  if (written != (ssize_t)msglen)
++    return ISIS_WARNING;
++
++  return ISIS_OK;
++}
+diff --git isisd/isis_trillvlans.c isisd/isis_trillvlans.c
+new file mode 100644
+index 0000000..2f66c16
+--- /dev/null
++++ isisd/isis_trillvlans.c
+@@ -0,0 +1,1207 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_trillvlans.c
++ *
++ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++
++#include <zebra.h>
++#include <command.h>
++#include <net/trill.h>
++
++#include "linklist.h"
++#include "vty.h"
++#include "dict.h"
++#include "memory.h"
++#include "log.h"
++#include "if.h"
++#include "prefix.h"
++#include "jhash.h"
++#include "stream.h"
++
++#include "isisd/isis_common.h"
++#include "isisd/isis_constants.h"
++#include "isisd/isis_circuit.h"
++#include "isisd/isis_flags.h"
++#include "isisd/isis_tlv.h"
++#include "isisd/isis_lsp.h"
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
++#include "isisd/isisd.h"
++#include "isisd/isis_pdu.h"
++#include "isisd/bool.h"
++#include "isisd/isis_misc.h"
++#include "isisd/isis_spf.h"
++#include "isisd/isis_adjacency.h"
++
++static int
++compute_vlan_ranges(uint8_t *vlans, int *vlan, int *start, int *end)
++{
++  int vlan_set;
++  int vlan_start = 0;
++  int prev_vlan = 0;
++
++  EACH_VLAN_R(vlans, *vlan, vlan_set)
++    {
++       if (vlan_start != 0) 
++         {
++	   if (vlan_set) 
++	     {
++               prev_vlan++;
++	       assert (prev_vlan == (*vlan));
++	       continue;
++	     }
++	   *start = vlan_start;
++	   *end = prev_vlan;
++	   return TRUE;
++         }
++       if (!vlan_set)
++         continue;
++       vlan_start = *vlan;
++       prev_vlan = *vlan;
++    }
++  return FALSE;
++}
++
++static void
++trill_del_enabled_vlans_listnode(void *data)
++{
++  XFREE(MTYPE_ISIS_TRILL_ENABLEDVLANS, data); 
++}
++
++static void
++trill_compute_enabled_vlans_subtlv(struct isis_circuit *circuit)
++{
++  unsigned int bytenum;
++  int span = 0;
++  int endspan = 0;
++  int size;
++  uint8_t byte;
++  uint8_t *byteptr;
++  unsigned int foundstartvlan = FALSE;
++  struct list *tlvdatalist;
++  struct trill_enabled_vlans_listnode *data;
++
++  tlvdatalist = list_new();
++  tlvdatalist->del = trill_del_enabled_vlans_listnode;
++
++  for (bytenum=0; bytenum < VLANS_ARRSIZE; bytenum++)
++    {
++       byte = circuit->vlans->enabled[bytenum];
++       if (byte == 0)
++         { 
++	   if (!foundstartvlan)
++             continue;
++	   /* Check for large span, efficient to use a new sub-TLV */
++           if (bytenum != (VLANS_ARRSIZE-1) && ((bytenum - endspan)
++              <= (TLFLDS_LEN + sizeof (struct trill_enabledvlans_subtlv))))
++             continue;
++	 }
++       else if (!foundstartvlan)
++         {
++           foundstartvlan = TRUE;
++	   span = endspan = bytenum;
++	   /* continue checking until we reach end of vlan bit array */
++	   if (bytenum != (VLANS_ARRSIZE-1))
++	     continue;
++         }
++       else 
++         {
++           assert(foundstartvlan);
++           endspan = bytenum;
++	   /* span shouldn't exceed max subtlv length */
++	   if (bytenum != (VLANS_ARRSIZE-1) && (endspan - span) < MAX_VLANS_SUBTLV_LEN)
++             continue;
++	 }
++
++       assert(foundstartvlan);
++       assert(endspan >= span);
++       size = sizeof(struct trill_enabled_vlans_listnode) + endspan - span + 1;
++       data = XMALLOC(MTYPE_ISIS_TRILL_ENABLEDVLANS, size);
++       data->len = size - sizeof(data->len);
++       data->tlvdata.start_vlan = htons(span*NBBY);
++       byteptr = (uint8_t *)&data[1];
++       while (endspan - span >= 0)
++         {
++            assert(byteptr <= (((uint8_t *)data) + size));
++            *byteptr = REVERSE_BYTE(circuit->vlans->enabled[span]);
++	    span++;
++	    byteptr++;
++	 }
++       listnode_add(tlvdatalist, data);
++       foundstartvlan = FALSE;
++    }
++
++  if (listcount(tlvdatalist) > 0)
++    circuit->vlans->enabled_vlans = tlvdatalist;
++  else
++    list_delete(tlvdatalist);
++}
++
++static void
++trill_del_appvlanfwders_listnode(void *data)
++{
++  XFREE(MTYPE_ISIS_TRILL_VLANFWDERS, data); 
++}
++
++static int
++trill_cmp_appvlanfwders(void *data1, void *data2)
++{
++  int vlan1;
++  int vlan2;
++
++  vlan1 = ntohs(((struct appointed_vlanfwder_subtlv *)data1)->vlan_start);
++  vlan2 = ntohs(((struct appointed_vlanfwder_subtlv *)data2)->vlan_start);
++  return (vlan1 < vlan2 ? -1:(vlan1 == vlan2 ? 0:1));
++}
++
++static void
++trill_add_vlanfwder(struct isis_circuit *circuit, int start, int end,
++    int nick, u_int32_t *hash)
++{
++  struct appointed_vlanfwder_subtlv *vlanfwder;
++
++  vlanfwder = XMALLOC (MTYPE_ISIS_TRILL_VLANFWDERS,
++      sizeof (struct appointed_vlanfwder_subtlv)); 
++  vlanfwder->appointee_nick = nick;
++  vlanfwder->vlan_start = htons(start);
++  vlanfwder->vlan_end = htons(end);
++  listnode_add_sort(circuit->vlans->appvlanfwders, vlanfwder);
++  *hash = jhash(vlanfwder, sizeof (struct appointed_vlanfwder_subtlv), *hash);
++}
++
++static void
++trill_compute_vlanfwders(struct isis_circuit *circuit, int *refresh)
++{
++  int vlan = VLAN_MIN;
++  int start;
++  int end;
++  int nick;
++  struct isis_adjacency *adj;
++  struct list *adjdb;
++  struct listnode *node;
++  struct listnode *nextnode;
++  struct appointed_vlanfwder_subtlv *vlanfwder;
++  struct appointed_vlanfwder_subtlv *prevvlanfwder;
++  u_int32_t prevhash = circuit->vlans->vlanfwdershash;
++  u_int32_t newhash = 0;
++
++  if (circuit->vlans->appvlanfwders != NULL) 
++    {
++      list_delete(circuit->vlans->appvlanfwders);
++      circuit->vlans->appvlanfwders = NULL;
++    }
++
++  if (circuit->area->trill->nick.name == RBRIDGE_NICKNAME_NONE)
++    {
++      *refresh = FALSE;
++      return;
++    }
++
++  circuit->vlans->appvlanfwders = list_new();
++  circuit->vlans->appvlanfwders->del = trill_del_appvlanfwders_listnode;
++  circuit->vlans->appvlanfwders->cmp = trill_cmp_appvlanfwders;
++
++  /*
++   * From the assigned VLAN forwarders among the adjacencies compute
++   * appointed VLAN forwarder sub-TLVs. We exclude VLANs assigned to
++   * ourself (the DR).
++   */
++  adjdb = circuit->u.bc.adjdb[TRILL_ISIS_LEVEL - 1];
++  for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
++    {
++      vlan = VLAN_MIN;
++      while (compute_vlan_ranges(adj->vlans->forwarder, &vlan, &start, &end))
++        { 
++          nick = sysid_to_nick(circuit->area, adj->sysid);
++	  if (nick != RBRIDGE_NICKNAME_NONE)
++            trill_add_vlanfwder(circuit, start, end, nick, &newhash);
++	}
++    }
++
++  circuit->vlans->vlanfwdershash = newhash;
++  *refresh = (newhash == prevhash ? FALSE:TRUE);
++
++  /*
++   * Compress the VLAN forwarder sub-TLVs by including missing VLANs in
++   * the ranges. We use the sorted appvlanfwders list to quickly determine
++   * the missing VLANs.
++   */
++  nick = 0;
++  prevvlanfwder = NULL;
++  for (ALL_LIST_ELEMENTS(circuit->vlans->appvlanfwders, node,
++      nextnode, vlanfwder))
++    {
++      if (nick != 0 && vlanfwder->appointee_nick == nick)
++        {
++          prevvlanfwder->vlan_end = vlanfwder->vlan_end;
++          trill_del_appvlanfwders_listnode(vlanfwder);
++          list_delete_node(circuit->vlans->appvlanfwders, node);
++	  continue;
++        }
++       nick = vlanfwder->appointee_nick;
++       prevvlanfwder = vlanfwder;
++    }
++}
++
++/*
++ * Clear all our info on VLAN forwarders. 
++ */
++static void
++trill_clear_vlanfwderinfo(struct isis_circuit *circuit)
++{
++  struct listnode *node;
++  struct isis_adjacency *adj;
++
++  /* Clear existing VLAN forwarder information */
++  memset (circuit->vlans->forwarder, 0, VLANS_ARRSIZE);
++
++  for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[TRILL_ISIS_LEVEL-1], node, adj))
++    memset (adj->vlans->forwarder, 0, VLANS_ARRSIZE);
++}
++
++/*
++ * TRILL function called when sending a hello frame on a TRILL circuit.
++ * Sends additional VLAN Hellos for TRILL based on VLANs we see Hellos on
++ * and from VLANs reported by other adjacencies. If we are DR then VLAN 
++ * forwarders are also computed. 
++ */
++void
++send_trill_vlan_hellos(struct isis_circuit *circuit)
++{
++  struct listnode *node;
++  struct list *adjdb;
++  struct isis_adjacency *adj;
++  u_int8_t txvlans[VLANS_ARRSIZE];
++  u_int8_t fwdvlans[VLANS_ARRSIZE];
++  u_int vlan_set;
++  int vlan;
++
++  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
++    return;
++
++  if (circuit->u.bc.is_dr[TRILL_ISIS_LEVEL - 1])
++    {
++      int refresh_lsp;
++
++      /* Update circuit's designated VLAN */
++      circuit->vlans->designated = circuit->vlans->our_designated;
++
++      trill_clear_vlanfwderinfo(circuit);
++
++      /* Appoint ourselves the VLAN forwarder for all our enabled VLANs */
++      memcpy(circuit->vlans->forwarder, circuit->vlans->enabled, VLANS_ARRSIZE);
++
++      /* Initialize the list of VLANs already assigned VLAN forwarder */
++      memcpy(fwdvlans, circuit->vlans->enabled, VLANS_ARRSIZE);
++
++      adjdb = circuit->u.bc.adjdb[TRILL_ISIS_LEVEL - 1];
++      for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
++        {
++          /* 
++           * If DR then appoint VLAN forwarder if no RBridge
++           * has been appointed yet to forward the particular VLAN.
++           */
++          EACH_VLAN_SET(adj->vlans->enabled, vlan, vlan_set)
++	   {
++              if (CHECK_VLAN(fwdvlans, vlan))
++                continue;
++
++	      /* 
++	       * Delegate the VLAN forwarding to the adjacency
++	       * as no other RBridge is forwarding the VLAN.
++	       */
++              SET_VLAN(adj->vlans->forwarder, vlan);
++	      SET_VLAN(fwdvlans, vlan);
++	   }
++        }
++
++      /*
++       * Based on the above VLAN forwarder appointments compute the VLAN
++       * forwarder TLVs. If VLAN forwarder info has changed then we also
++       * generate new LSPs.
++       */ 
++      trill_compute_vlanfwders(circuit, &refresh_lsp);
++      if (refresh_lsp)
++        lsp_regenerate_schedule (circuit->area);
++
++      /* DR sends hellos on all its enabled VLANs */
++      memcpy(txvlans, circuit->vlans->enabled, VLANS_ARRSIZE);
++      SET_VLAN(txvlans, circuit->vlans->designated);
++    }
++  else 
++    {
++      /*
++       * Non-DR sends hellos on designated VLAN (if enabled)
++       * and on all VLANs it is the appointed forwarder.
++       */
++      bzero(txvlans, VLANS_ARRSIZE);
++      if (CHECK_VLAN(circuit->vlans->enabled, circuit->vlans->designated))
++        SET_VLAN(txvlans, circuit->vlans->designated);
++      MERGE_VLANS(txvlans, vlan, circuit->vlans->forwarder);
++    }
++
++  /* Send hellos */
++  EACH_VLAN_SET(txvlans, vlan, vlan_set)
++    {
++      circuit->vlans->tx_tci = vlan;
++      send_hello(circuit, TRILL_ISIS_LEVEL);
++    }
++
++  /* Re-set circuit to use the link's designated VLAN for all IS-IS frames */
++  circuit->vlans->tx_tci = VLANTCI(circuit->vlans->designated);
++
++  /* Compute enabled VLANs subtlvs (performed only once) */
++  if (circuit->vlans->enabled_vlans == NULL)
++    trill_compute_enabled_vlans_subtlv(circuit);
++}
++
++static void
++trill_del_vlansreachablelist(void *obj)
++{
++  XFREE (MTYPE_ISIS_TRILL_VLANSREACHABLE, obj);
++}
++
++/*
++ * Compute VLAN filter lists by recursively going over the nodes in the DT.
++ * If rvertex is set then we stop at the matching node in the DT otherwise
++ * we stop until all children nodes are covered.
++ */
++static void
++trill_compute_vlanfilterlist(struct isis_area *area, struct isis_spftree *rdtree,
++  struct isis_vertex *vertex, struct isis_vertex *rvertex, uint8_t *filtermap)
++{
++  nicknode_t *nicknode;
++  struct isis_vertex *cvertex;
++  struct listnode *node;
++  int idx;
++
++  if (!listnode_lookup (rdtree->paths, vertex))
++    return;
++  if (vertex->type != VTYPE_NONPSEUDO_IS &&
++      vertex->type != VTYPE_NONPSEUDO_TE_IS)
++    return;
++
++  nicknode = trill_nicknode_lookup(area, sysid_to_nick(area, vertex->N.id));
++  if (nicknode == NULL)
++	  return;
++
++  MERGE_VLANS(filtermap, idx, nicknode->info.vlans_forwarder);
++
++  if (rvertex != NULL &&
++      (memcmp(vertex->N.id, rvertex->N.id, ISIS_SYS_ID_LEN) == 0))
++    return;
++
++  if (vertex->children != NULL)
++    {
++       for (ALL_LIST_ELEMENTS_RO(vertex->children, node, cvertex))
++         trill_compute_vlanfilterlist(area, rdtree, cvertex,
++		 rvertex, filtermap);
++    }
++}
++
++/*
++ * Creates TRILL VLAN filter lists for each of our adjacencies on 
++ * the given node's distribution tree (DT). A TRILL VLAN filter list
++ * for an adjacency on a distribution tree is the set of all VLANs that
++ * are reachable downstream via the adjacency.
++ */
++void
++trill_create_vlanfilterlist(struct isis_area *area, nicknode_t *nicknode)
++{
++  struct listnode *node;
++  struct listnode *lnode;
++  struct list *adjlist;
++  struct isis_vertex *vertex;
++  struct isis_vertex *rbvertex;
++  struct isis_vertex *adjvertex;
++  struct isis_spftree *rdtree;
++  void *listdata;
++  u_int16_t adjnick;
++  nicknode_t *adjnode;
++  int adjishead;
++  struct list *vlanfilterlist;
++  uint8_t *vlanfiltermap;
++
++  if (nicknode == NULL) 
++   {
++     adjlist = area->trill->adjnodes;
++     rdtree = area->spftree[TRILL_ISIS_LEVEL-1];
++     if (area->trill->vlans_reachable != NULL)
++       {
++         list_delete(area->trill->vlans_reachable);
++	 area->trill->vlans_reachable = NULL;
++       }
++   }
++  else
++   {
++     adjlist = nicknode->adjnodes;
++     rdtree = nicknode->rdtree;
++     if (nicknode->vlans_reachable != NULL)
++       {
++         list_delete(nicknode->vlans_reachable);
++	 nicknode->vlans_reachable = NULL;
++       }
++   }
++  
++  if (adjlist == NULL)
++    return;
++
++  vlanfilterlist = list_new();
++  vlanfilterlist->del = trill_del_vlansreachablelist;
++
++  /*
++   * For each of the adjacencies compute VLAN filter list
++   * on the DT with nicknode as the root.
++   */
++  for (ALL_LIST_ELEMENTS_RO (adjlist, node, listdata))
++    {
++      adjnick = (u_int16_t)(u_long)listdata;
++      adjnode = trill_nicknode_lookup(area, adjnick);
++      if (adjnode == NULL)
++        {
++          zlog_warn("trill_create_vlanfilterlist: adjlist node lookup failed.");
++          list_delete(vlanfilterlist);
++	  return;
++	}
++
++      /*
++       * Determine if the adjacency is towards the parent (adjishead is TRUE)
++       * or if the adjacency is our child node on the DT with nicknode as root.
++       * Computing this direction determines how we search for reachable VLANs.
++       */
++      adjishead = FALSE;
++      rbvertex = adjvertex = NULL;
++      for (ALL_LIST_ELEMENTS_RO (rdtree->paths, lnode, vertex))
++        {
++          if (vertex->type != VTYPE_NONPSEUDO_IS &&
++	      vertex->type != VTYPE_NONPSEUDO_TE_IS)
++	    continue;
++	  /* We found the adjacency node in the tree */
++	  if (memcmp (vertex->N.id, adjnode->info.sysid, ISIS_SYS_ID_LEN) == 0)
++            adjvertex = vertex;
++	  /* We found our node in the DT with nicknode as root */
++	  else if (memcmp (vertex->N.id, area->isis->sysid,
++	      ISIS_SYS_ID_LEN) == 0)
++	    rbvertex = vertex;
++	  else
++            continue;
++	  /* If we found adjacency node first then we set adjishead to TRUE */
++	  if (!adjishead  && adjvertex != NULL && rbvertex == NULL)
++             adjishead = TRUE;
++        }
++
++      if (rbvertex == NULL || adjvertex == NULL)
++        {
++          zlog_warn("trill_create_vlanfilterlist: rbvertex adjvertex lookup failed.");
++          list_delete(vlanfilterlist);
++	  return;
++        }
++
++      vlanfiltermap = XCALLOC(MTYPE_ISIS_TRILL_VLANSREACHABLE, VLANS_ARRSIZE);
++      if (adjishead == TRUE)
++        {
++	  /*
++	   * If adjacency is head then compute VLAN filter lists from the root
++	   * node and cover all nodes except all the children of the adjacency
++	   * node. This covers all nodes in the tree except the adjacency
++	   * branch.
++	   */
++          trill_compute_vlanfilterlist(area, rdtree,
++		listgetdata(listhead (rdtree->paths)), adjvertex, vlanfiltermap);
++        }
++      else
++        {
++	  /*
++	   * Adjacency is a child node of ours in the DT so to compute all the VLANs
++	   * reachable through the child we just go over all the children nodes.
++	   */
++          trill_compute_vlanfilterlist(area, rdtree, adjvertex, NULL, vlanfiltermap);
++        }
++      listnode_add(vlanfilterlist, vlanfiltermap);
++    }
++
++  /* Must compute a VLAN filter map per adjacency */
++  if (listcount(vlanfilterlist) == listcount(adjlist))
++    {
++       if (nicknode == NULL)
++         area->trill->vlans_reachable = vlanfilterlist;
++       else
++         nicknode->vlans_reachable = vlanfilterlist;
++    }
++}
++
++static void
++trill_parse_enabled_vlans_subtlv(struct isis_adjacency *adj, u_int8_t *ptr,
++	u_int8_t len)
++{
++   u_int8_t *end;
++   struct trill_enabledvlans_subtlv *vlanmap;
++   int vlan;
++   u_int8_t byte;
++   int idx;
++
++   end = ptr + len;
++   vlanmap = (struct trill_enabledvlans_subtlv *)ptr;
++   vlan = VLANTCI(ntohs(vlanmap->start_vlan));
++
++   ptr += sizeof (struct trill_enabledvlans_subtlv);
++   while (ptr < end)
++     {
++        byte = *ptr++;
++	if (byte == 0)
++	  {
++            vlan += NBBY;
++            continue;
++	  }
++
++	for (idx = NBBY-1; idx >= 0; idx--)
++	  {
++            if ((byte & (1<<idx)) != 0)
++              SET_VLAN(adj->vlans->enabled, vlan);
++            vlan++;
++	  }
++     }
++}
++
++static void
++inhibit_free(void *arg)
++{
++  XFREE (MTYPE_ISIS_TRILL_INHIB, arg);
++}
++
++static void
++remove_inhib(struct isis_circuit *circuit, u_int16_t rxvlan)
++{
++  struct trill_inhibit_vlan *inhib;
++  struct listnode *node, *nextnode;
++  struct trill_circuit_vlans *cvlans = circuit->vlans;
++
++  for (ALL_LIST_ELEMENTS (cvlans->inhibit_vlans, node, nextnode, inhib))
++    {
++      if (inhib->vlan == rxvlan)
++	{
++	  list_delete_node (cvlans->inhibit_vlans, node);
++	  inhibit_free (inhib);
++	}
++    }
++  if (list_isempty (cvlans->inhibit_vlans) && cvlans->inhibit_all == 0 &&
++      cvlans->inhibit_thread != NULL)
++    {
++      thread_cancel (cvlans->inhibit_thread);
++      cvlans->inhibit_thread = NULL;
++    }
++}
++
++/*
++ * Update the previous and new lists of VLANs for which we're the appointed
++ * forwarder, based on the inhibited VLAN list.
++ *
++ * If the VLAN is set in the new list, then the inhibiting entry is still
++ * valid; clear it from that new list.
++ *
++ * If the VLAN is not set in the new list, then the DR has revoked our
++ * appointment, so the entry must be removed from the inhibit list, and treated
++ * as though it were previously enabled.
++ */
++static void
++check_disabled_inhib (struct isis_circuit *circuit, u_int8_t *prevvlans,
++    u_int8_t *newvlans)
++{
++  struct trill_inhibit_vlan *inhib;
++  struct listnode *node, *nextnode;
++  struct trill_circuit_vlans *cvlans = circuit->vlans;
++
++  for (ALL_LIST_ELEMENTS (cvlans->inhibit_vlans, node, nextnode, inhib))
++    {
++      if (CHECK_VLAN (newvlans, inhib->vlan))
++	{
++	  CLEAR_VLAN (newvlans, inhib->vlan);
++	}
++      else
++	{
++	  SET_VLAN (prevvlans, inhib->vlan);
++	  list_delete_node (cvlans->inhibit_vlans, node);
++	  inhibit_free (inhib);
++	}
++    }
++  if (list_isempty (cvlans->inhibit_vlans) && cvlans->inhibit_all == 0 &&
++      cvlans->inhibit_thread != NULL)
++    {
++      thread_cancel (cvlans->inhibit_thread);
++      cvlans->inhibit_thread = NULL;
++    }
++}
++
++static int
++uninhibit_vlan (struct thread *thread)
++{
++  struct isis_circuit *circuit;
++  struct trill_circuit_vlans *cvlans;
++  struct listnode *node = NULL;
++  struct trill_inhibit_vlan *inhib = NULL;
++  int mintime, alltime;
++  char reenabled = FALSE;
++  time_t now;
++
++  circuit = THREAD_ARG (thread);
++  cvlans = circuit->vlans;
++
++  now = time (NULL);
++  alltime = cvlans->inhibit_all - now;
++  if (cvlans->inhibit_all != 0 && alltime <= 0)
++    {
++      cvlans->inhibit_all = 0;
++      reenabled = TRUE;
++    }
++
++  if (cvlans->inhibit_vlans != NULL)
++    {
++      while ((node = listhead (cvlans->inhibit_vlans)) != NULL)
++	{
++	  inhib = listgetdata (node);
++	  if ((int)(inhib->reenable - now) > 0)
++	    break;
++	  reenabled = TRUE;
++	  SET_VLAN (cvlans->forwarder, inhib->vlan);
++	  list_delete_node (cvlans->inhibit_vlans, node);
++	  inhibit_free (inhib);
++	}
++    }
++
++  /* If we've reenabled something, then tell the kernel */
++  if (reenabled && cvlans->inhibit_all == 0)
++    trill_set_vlan_forwarder (circuit, cvlans->forwarder);
++
++  /* Set up the next expiry */
++  if (node == NULL && cvlans->inhibit_all == 0)
++    cvlans->inhibit_thread = NULL;
++  else
++    {
++      mintime = node == NULL ? alltime : inhib->reenable - now;
++      if (cvlans->inhibit_all != 0 && alltime < mintime)
++	mintime = alltime;
++      cvlans->inhibit_thread = thread_add_timer (master, uninhibit_vlan,
++	  circuit, mintime);
++    }
++  return ISIS_OK;
++}
++
++void
++trill_inhib_all(struct isis_circuit *circuit)
++{
++  u_int8_t nullvlans[VLANS_ARRSIZE];
++  struct trill_circuit_vlans *cvlans = circuit->vlans;
++  int interval;
++
++  memset (nullvlans, 0, sizeof nullvlans);
++  trill_set_vlan_forwarder (circuit, nullvlans);
++
++  interval = 15;
++  cvlans->inhibit_all = time (NULL) + interval;
++
++  THREAD_TIMER_ON (master, cvlans->inhibit_thread, uninhibit_vlan, circuit,
++      interval);
++}
++
++static void
++add_inhib(struct isis_circuit *circuit, u_int16_t rxvlan)
++{
++  struct trill_circuit_vlans *cvlans = circuit->vlans;
++  struct trill_inhibit_vlan *inhib;
++  int interval;
++
++  interval = 5 * circuit->hello_interval[0];
++
++  inhib = XMALLOC (MTYPE_ISIS_TRILL_INHIB, sizeof (*inhib));
++  inhib->vlan = rxvlan;
++  inhib->reenable = time (NULL) + interval;
++  listnode_add (cvlans->inhibit_vlans, inhib);
++
++  CLEAR_VLAN (cvlans->forwarder, rxvlan);
++  if (cvlans->inhibit_all == 0)
++    trill_set_vlan_forwarder (circuit, cvlans->forwarder);
++
++  THREAD_TIMER_ON (master, cvlans->inhibit_thread, uninhibit_vlan, circuit,
++      interval);
++}
++
++/*
++ * Process incoming hello packets and process port capability TLVs.
++ */
++void
++trill_process_hello(struct isis_adjacency *adj, struct list *portcaps)
++{
++  u_int8_t subtlv;
++  u_int8_t *ptr;
++  int len;
++  int subtlv_len;
++  struct listnode *node;
++  struct port_capability_tlv *pcap;
++  struct trill_vlanflags_subtlv *vlanflags = NULL;
++  struct isis_circuit *circuit = adj->circuit;
++  struct trill_circuit_vlans *cvlans = circuit->vlans;
++  struct list *vlanfwders = NULL;
++  int vflags_stlv_found = FALSE;
++  int adj_is_dr = FALSE;
++  int dis_nick = RBRIDGE_NICKNAME_NONE;
++
++  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
++    return;
++
++  if ((!circuit->u.bc.is_dr[TRILL_ISIS_LEVEL - 1]) &&
++        memcmp(circuit->u.bc.l1_desig_is, adj->sysid, ISIS_SYS_ID_LEN) == 0) {
++    adj_is_dr = TRUE;
++    dis_nick = sysid_to_nick(circuit->area, adj->sysid);
++  }
++
++  memset(adj->vlans->enabled, 0, VLANS_ARRSIZE);
++  for (ALL_LIST_ELEMENTS_RO (portcaps, node, pcap))
++    {
++       len = pcap->len;
++       ptr = pcap->value;
++       while (len > TLFLDS_LEN)
++         {
++            subtlv = *ptr; ptr++; len--;
++	    subtlv_len = *ptr; ptr++; len--;
++	    if (subtlv_len > len)
++	      break;
++
++	    switch (subtlv)
++	      {
++	      case PCSTLV_VLANS:
++	         if (vflags_stlv_found == TRUE)
++                   {
++		     zlog_warn("trill_process_hello: received more than"
++			    " one VLANs and Flags sub-TLV");
++		     vlanflags = NULL;
++		   }
++		 else if (subtlv_len == PCSTLV_VLANS_LEN && vlanflags == NULL)
++		   vlanflags = (struct trill_vlanflags_subtlv *)ptr;
++		 len -= subtlv_len;
++		 ptr += subtlv_len;
++		 vflags_stlv_found = TRUE;
++	         break;
++
++	      case PCSTLV_APPFORWARDERS:
++		 if ((subtlv_len % sizeof (struct appointed_vlanfwder_subtlv))
++		     != 0)
++		   {
++		     zlog_warn("trill_process_hello: received invalid length:%d"
++			" appointed forwarders sub-TLV", subtlv_len);
++		     len -= subtlv_len;
++		     ptr += subtlv_len;
++		     break;
++		   }
++		 if (vlanfwders == NULL)
++		   vlanfwders = list_new();
++		 while (subtlv_len > 0)
++		   {
++		      listnode_add (vlanfwders, ptr);
++		      ptr += sizeof (struct appointed_vlanfwder_subtlv);
++		      subtlv_len -= sizeof (struct appointed_vlanfwder_subtlv);
++		      len -= sizeof (struct appointed_vlanfwder_subtlv);
++		   }
++		 break;
++
++	      case PCSTLV_ENABLEDVLANS:
++	        if (subtlv_len < PCSTLV_ENABLEDVLANS_MIN_LEN)	
++		  zlog_warn("trill_process_hello:"
++		     " received invalid length (too small):%d"
++		     " enabled VLANS sub-TLV", subtlv_len);
++		else
++		  trill_parse_enabled_vlans_subtlv(adj, ptr, subtlv_len);
++	        len -= subtlv_len;
++	        ptr += subtlv_len;
++		break;
++
++              default:
++		 len -= subtlv_len;
++		 ptr += subtlv_len;
++		 break;
++	      }
++	 }
++   }
++
++  /* Process appointed VLAN forwarders sub-TLV */
++  if (adj_is_dr)
++    {
++      u_int8_t *fwdvlans;
++      u_int8_t *enabledvlans;
++      u_int8_t prevfwdvlans[VLANS_ARRSIZE];
++      u_int8_t appvlans[VLANS_ARRSIZE];
++      struct appointed_vlanfwder_subtlv *appvlanfwder;
++      struct isis_adjacency *nadj;
++      int vlan;
++      int vbyte;
++      int vlanstart;
++      int vlanend;
++      u_char *sysid;
++
++      memcpy(prevfwdvlans, cvlans->forwarder, VLANS_ARRSIZE);
++      bzero(appvlans, sizeof (appvlans));
++
++      /* Clear existing VLAN forwarder information */
++      trill_clear_vlanfwderinfo(circuit);
++
++      if (vlanfwders != NULL)
++	for (ALL_LIST_ELEMENTS_RO (vlanfwders, node, appvlanfwder))
++        {
++	   /* Disregard any appointed VLAN forwarders to the DR */
++           if (appvlanfwder->appointee_nick == dis_nick)
++             continue;
++
++           if (appvlanfwder->appointee_nick == circuit->area->trill->nick.name)
++             {
++               sysid = circuit->area->isis->sysid;
++	       fwdvlans = cvlans->forwarder;
++	       enabledvlans = cvlans->enabled;
++	     }
++	   else
++	     {
++	       sysid = nick_to_sysid (circuit->area, appvlanfwder->appointee_nick);
++	       if (!sysid)
++                 continue;
++	       if ((nadj = isis_adj_lookup (sysid,
++	           circuit->u.bc.adjdb[TRILL_ISIS_LEVEL-1])) == NULL)
++	         continue;
++	       fwdvlans = nadj->vlans->forwarder;
++	       enabledvlans = nadj->vlans->enabled;
++	     }
++
++	   vlanstart = VLANTCI(ntohs(appvlanfwder->vlan_start));
++	   vlanend = VLANTCI(ntohs(appvlanfwder->vlan_end));
++
++	   /* Only accept VLANs the RBridge has advertised as enabled */
++	   for (vlan = vlanstart; vlan <= vlanend; vlan++)
++             if (CHECK_VLAN(enabledvlans, vlan)) 
++	       {
++                 SET_VLAN (fwdvlans, vlan);
++                 SET_VLAN (appvlans, vlan);
++	       }
++        }
++
++      /*
++       * Determine the VLANs forwarded by the adj that is the DR: they are
++       * all the VLANs enabled in the DR minus the VLANs that have appointed
++       * VLAN forwarders on the link.
++       */
++      for (vbyte = 0; vbyte < VLANS_ARRSIZE; vbyte++)
++        adj->vlans->forwarder[vbyte] =
++	    adj->vlans->enabled[vbyte] & ~appvlans[vbyte];
++
++      /*
++       * If there are any inhibited VLANs, then check whether we've lost AF
++       * status for them.  If so, then remove the inhibiting entry; it's no
++       * longer valid.  If not, then remove from new forwarder list.
++       */
++      if (cvlans->inhibit_vlans != NULL)
++	check_disabled_inhib (circuit, prevfwdvlans, cvlans->forwarder);
++
++      /*
++       * If the set of VLANs for which we've been appointed as forwarder has
++       * changed, then regenerate new LSPs with new AF bits and deal with AF
++       * status changes.
++       */
++      if (memcmp (prevfwdvlans, cvlans->forwarder, VLANS_ARRSIZE))
++	{
++	  int lost_any, vbit;
++	  u_int8_t vval;
++	  struct isis_circuit *ocir;
++
++	  /*
++	   * Compute the set of VLANs for which we're forwarder for some other
++	   * circuit.
++	   */
++	  bzero (appvlans, sizeof (appvlans));
++	  for (ALL_LIST_ELEMENTS_RO (circuit->area->circuit_list, node, ocir))
++	    {
++	      if (ocir != circuit)
++		{
++		  for (vbyte = 0; vbyte < VLANS_ARRSIZE; vbyte++)
++		    appvlans[vbyte] |= ocir->vlans->forwarder[vbyte];
++		}
++	    }
++
++	  /*
++	   * For all VLANs where we've lost AF status, increment the lost
++	   * counter and flush bridge forwarding entries learned directly over
++	   * this circuit for this VLAN.
++	   */
++	  lost_any = FALSE;
++	  for (vbyte = 0; vbyte < VLANS_ARRSIZE; vbyte++)
++	    {
++	      vval = prevfwdvlans[vbyte] & ~cvlans->forwarder[vbyte];
++	      if (vval != 0)
++		{
++		  lost_any = TRUE;
++		  for (vbit = 0; vbit < 8; vbit++)
++		    {
++		      if (vval & (1 << vbit))
++			{
++			  vlan = vbyte * 8 + vbit;
++			  trill_port_flush (circuit, vlan);
++			  if (!CHECK_VLAN (appvlans, vlan))
++			    trill_nick_flush (circuit, vlan);
++			}
++		    }
++		}
++	    }
++	  if (lost_any)
++	    {
++	      /* XXX carlsonj bump lost counter here */
++	      trill_send_tc_bpdus (circuit);
++	    }
++	  lsp_regenerate_schedule (circuit->area);
++	}
++    }
++
++  if (vlanflags != NULL)
++    {
++      int outervlan, rxvlan;
++
++      /*
++       * First get the flags stored in outer_vlan.  Check for a conflict if
++       * we've been set as the appointed forwarder.
++       */
++      outervlan = ntohs (vlanflags->outer_vlan);
++      rxvlan = VLANTCI (cvlans->rx_tci);
++      if ((outervlan & TVFFO_AF) && CHECK_VLAN (cvlans->forwarder, rxvlan))
++	{
++	  /* The inhibited VLANs list is created just once; it's rare */
++	  if (cvlans->inhibit_vlans == NULL)
++	    {
++	      cvlans->inhibit_vlans = list_new ();
++	      cvlans->inhibit_vlans->del = inhibit_free;
++	    }
++	  /* Remove any stale entries for this VLAN. */
++	  remove_inhib (circuit, rxvlan);
++	  /* Now add a new entry for the VLAN */
++	  add_inhib (circuit, rxvlan);
++	}
++
++      adj->vlans->designated = VLANTCI(ntohs(vlanflags->desig_vlan));
++      outervlan = VLANTCI(outervlan);
++      SET_VLAN(adj->vlans->seen, outervlan);
++      SET_VLAN(adj->vlans->seen, VLANTCI(cvlans->rx_tci));
++
++      /* If Adj is DR set circuit's designated link */
++      if (adj_is_dr)
++        {
++	  cvlans->designated = adj->vlans->designated;
++	  cvlans->tx_tci = VLANTCI(cvlans->designated);
++	}
++    }
++  if (vlanfwders != NULL)
++    list_delete (vlanfwders);
++}
++
++/* Add TRILL VLAN TLVs in TRILL IS-IS hellos */
++int
++tlv_add_trill_vlans(struct isis_circuit *circuit)
++{
++  struct stream *stream = circuit->snd_stream;
++  struct trill_vlanflags_subtlv vlanflags;
++  uint16_t outervlan;
++  struct listnode *node;
++  size_t tlvstart;
++  struct trill_enabled_vlans_listnode *evlans;
++  struct appointed_vlanfwder_subtlv *vlanfwder;
++  int rc;
++
++  if (circuit->circ_type != CIRCUIT_T_BROADCAST)
++    return ISIS_OK;
++
++  tlvstart = stream_get_endp (stream);
++  rc = add_tlv(PORT_CAPABILITY, 0, NULL, stream);
++  if (rc != ISIS_OK)
++    return rc;
++
++  outervlan = VLANTCI(circuit->vlans->tx_tci);
++  if (CHECK_VLAN(circuit->vlans->forwarder, outervlan))
++    outervlan |= TVFFO_AF;
++  vlanflags.outer_vlan = htons(outervlan);
++  vlanflags.desig_vlan = htons(circuit->vlans->designated);
++  rc = add_subtlv (PCSTLV_VLANS, sizeof (vlanflags), (uchar_t *)&vlanflags,
++      tlvstart, stream);
++  if (rc != ISIS_OK)
++    return rc;
++
++  if (circuit->vlans->enabled_vlans != NULL)
++    {
++       for (ALL_LIST_ELEMENTS_RO(circuit->vlans->enabled_vlans, node, evlans))
++         {
++            rc = add_subtlv(PCSTLV_ENABLEDVLANS, evlans->len,
++               (uchar_t *)&evlans->tlvdata, tlvstart, stream);
++	    if (rc == ISIS_ERROR)
++               return rc;
++	    if (rc == ISIS_WARNING)
++	      {
++                 tlvstart = stream_get_endp (stream);
++                 rc = add_tlv(PORT_CAPABILITY, 0, NULL, stream);
++                 if (rc != ISIS_OK)
++                   return rc;
++                 rc = add_subtlv(PCSTLV_ENABLEDVLANS, evlans->len,
++                    (uchar_t *)&evlans->tlvdata, tlvstart, stream);
++                 if (rc != ISIS_OK)
++                   return rc;
++	      }
++         }
++    }
++
++  if (!circuit->u.bc.is_dr[TRILL_ISIS_LEVEL - 1] ||
++       circuit->vlans->appvlanfwders == NULL)
++      return rc;
++
++  for (ALL_LIST_ELEMENTS_RO(circuit->vlans->appvlanfwders, node,
++      vlanfwder))
++    {
++       rc = add_subtlv(PCSTLV_APPFORWARDERS, sizeof(*vlanfwder),
++          (uchar_t *)vlanfwder, tlvstart, stream);
++       if (rc == ISIS_ERROR)
++          return rc;
++       if (rc == ISIS_WARNING)
++         {
++           tlvstart = stream_get_endp (stream);
++           rc = add_tlv(PORT_CAPABILITY, 0, NULL, stream);
++           if (rc != ISIS_OK)
++             return rc;
++           rc = add_subtlv(PCSTLV_APPFORWARDERS, sizeof(*vlanfwder),
++              (uchar_t *)vlanfwder, tlvstart, stream);
++           if (rc != ISIS_OK)
++             return rc;
++	 }
++    }
++  return rc;
++}
++
++/*
++ * show trill circuits command to display TRILL circuit information.
++ */
++void
++trill_circuits_print_all (struct vty *vty, struct isis_area *area)
++{
++  struct listnode *node;
++  struct isis_circuit *circuit;
++  int vlan_count = 0;
++  int vlan_set;
++  int vlan;
++
++  if (area->circuit_list == NULL)
++    return;
++
++  for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
++    {
++
++      vty_out (vty, "%sInterface %s:%s", VTY_NEWLINE, circuit->interface->name, VTY_NEWLINE);
++      vty_out (vty, "PVID:%d  Our Designated VLAN:%d Designated VLAN:%d%s",
++	 circuit->vlans->pvid, circuit->vlans->designated, circuit->vlans->our_designated,
++	 VTY_NEWLINE);
++
++      vty_out (vty, "VLAN Forwarder: ");
++      EACH_VLAN_SET(circuit->vlans->forwarder, vlan, vlan_set)
++        {
++	   vlan_count++;
++	   if (vlan_count % 8 == 0)
++             vty_out(vty, "%s               ", VTY_NEWLINE);
++           vty_out (vty, "%d ", vlan); 
++        }
++      vty_out (vty, "%sEnabled VLANs: ", VTY_NEWLINE);
++      vlan_count = 0;
++      EACH_VLAN_SET(circuit->vlans->enabled, vlan, vlan_set)
++        {
++	   vlan_count++;
++	   if (vlan_count % 8 == 0)
++             vty_out(vty, "%s               ", VTY_NEWLINE);
++           vty_out (vty, "%d ", vlan); 
++        }
++      vty_out (vty, "%s", VTY_NEWLINE);
++    }
++}
++
++DEFUN (trill_isis_vlan,
++       trill_isis_vlan_cmd,
++       "trill isis vlan <1-4094>",
++       "TRILL IS-IS commands\n"
++       "Set TRILL IS-IS VLAN\n"
++       "VLAN ID\n")
++{
++  struct isis_circuit *circuit;
++  struct interface *ifp;
++
++  ifp = vty->index;
++  circuit = ifp->info;
++  if (circuit == NULL)
++    {
++      return CMD_WARNING;
++    }
++  assert (circuit);
++
++  SET_VLAN(circuit->vlans->enabled, atoi(argv[0]));
++  return CMD_SUCCESS;
++}
++
++DEFUN (trill_isis_no_vlan,
++       trill_isis_no_vlan_cmd,
++       "trill isis no vlan <1-4094>",
++       "TRILL IS-IS commands\n"
++       "Clear TRILL IS-IS VLAN\n"
++       "VLAN ID\n")
++{
++  struct isis_circuit *circuit;
++  struct interface *ifp;
++
++  ifp = vty->index;
++  circuit = ifp->info;
++  if (circuit == NULL)
++    {
++      return CMD_WARNING;
++    }
++  assert (circuit);
++
++  CLEAR_VLAN(circuit->vlans->enabled, atoi(argv[0]));
++  return CMD_SUCCESS;
++}
++
++DEFUN (trill_isis_pvid,
++       trill_isis_pvid_cmd,
++       "trill isis pvid <1-4094>",
++       "TRILL IS-IS commands\n"
++       "Set TRILL IS-IS Native VLAN (PVID) \n"
++       "PVID\n")
++{
++  struct isis_circuit *circuit;
++  struct interface *ifp;
++
++  ifp = vty->index;
++  circuit = ifp->info;
++  if (circuit == NULL)
++    {
++      return CMD_WARNING;
++    }
++  assert (circuit);
++
++  circuit->vlans->pvid = atoi(argv[0]);
++  return CMD_SUCCESS;
++}
++
++void
++install_trill_vlan_elements (void)
++{
++  install_element (INTERFACE_NODE, &trill_isis_vlan_cmd);
++  install_element (INTERFACE_NODE, &trill_isis_no_vlan_cmd);
++  install_element (INTERFACE_NODE, &trill_isis_pvid_cmd);
++}
+diff --git isisd/isis_vlans.h isisd/isis_vlans.h
+new file mode 100644
+index 0000000..9a54e58
+--- /dev/null
++++ isisd/isis_vlans.h
+@@ -0,0 +1,128 @@
++/*
++ * IS-IS Rout(e)ing protocol - isis_vlans.h
++ *
++ * This program is free software; you can redistribute it and/or modify it 
++ * under the terms of the GNU General Public Licenseas published by the Free 
++ * Software Foundation; either version 2 of the License, or (at your option) 
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,but WITHOUT 
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
++ * more details.
++
++ * You should have received a copy of the GNU General Public License along 
++ * with this program; if not, write to the Free Software Foundation, Inc., 
++ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
++ */
++#ifndef  ISIS_VLANS_H
++#define	ISIS_VLANS_H
++
++/* TRILL IS-IS VLANs */
++#define NO_TCI 0
++#define DFLT_VLAN 1
++#define VLANS_SIZE (1<<12)
++#define VLANS_ARRSIZE (VLANS_SIZE/NBBY)
++#define VLAN_MAX 4094
++#define VLAN_MIN 1
++#define VLANTCI(p) ((p)&((VLANS_SIZE)-1))
++
++#define VLANBIT(v) ((v) % NBBY)
++#define VLANBYTE(v) ((v)/NBBY)
++#define CHECK_VLAN(p, v) ((p)[VLANBYTE(v)] & (1<<VLANBIT(v)))
++#define SET_VLAN(p, v) \
++       	do { \
++          if ((v) > 0) \
++       	    ((p)[VLANBYTE(v)] |= 1<<VLANBIT(v)); \
++	} while (0)
++#define CLEAR_VLAN(p, v) \
++	do { \
++          if ((v) > 0) \
++	    ((p)[VLANBYTE(v)] &= ~(1<<VLANBIT(v))); \
++	} while (0)
++
++#define EACH_VLAN(p, v, c) \
++	for ( \
++	(v) = VLAN_MIN; \
++        ((v) <= VLAN_MAX) && \
++	((c) = CHECK_VLAN((p), (v)), 1); \
++	(v)++ )
++
++#define EACH_VLAN_SET(p, v, c) \
++	EACH_VLAN(p, v, c) \
++        if (c) \
++
++#define EACH_VLAN_R(p, v, c) \
++	for (; \
++        ((v) <= VLAN_MAX) && \
++	((c) = CHECK_VLAN((p), (v)), 1); \
++	(v)++ ) \
++
++#define EACH_VLAN_SET_R(p, v, c) \
++	EACH_VLAN_R(p, v, c) \
++	if (c) \
++
++#define MERGE_VLANS(p, v, z) \
++	do { \
++	  for ((v) = 0; (v) < VLANS_ARRSIZE; (v)++) \
++	   (p)[(v)] |= (z)[(v)]; \
++	} while (0)
++
++/* source: http://graphics.stanford.edu/~seander/bithacks.html */
++#define REVERSE_BYTE(v) ((uint8_t)((((v) * 0x0802LU & 0x22110LU) | \
++       	((v) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16));
++
++#define MAX_VLANS_SUBTLV_LEN 240 /* 255 - bytes for type, len, start vlan */
++
++/* Encoded enabled VLANs information for Hello PDU PCTLV_ENABLEDVLANS */
++struct trill_enabled_vlans_listnode
++{
++  uint16_t len;
++  struct trill_enabledvlans_subtlv tlvdata;
++};
++
++/*
++ * Entry on the inhibit_vlan list.  This should be rarely used; it corresponds
++ * to receipt of a Hello message from some other node claiming AF status for a
++ * VLAN for which we were appointed.
++ */
++struct trill_inhibit_vlan
++{
++  time_t reenable;
++  u_int16_t vlan;
++};
++
++struct trill_circuit_vlans
++{
++  u_int16_t pvid;	 /* Default/Native VLAN for untagged frames */
++  u_int16_t designated;	 /* Designated VLAN for the circuit */
++  u_int16_t our_designated; /* Our choice for Designated VLAN */
++  u_int8_t enabled[VLANS_ARRSIZE]; /* VLANs we could be the forwarder */
++  u_int8_t forwarder[VLANS_ARRSIZE]; /* VLANs for which we are the forwarder */
++  u_int16_t rx_tci; /* PCP, CFI, VID */
++  u_int16_t tx_tci; /* PCP, CFI, VID */
++  struct list *enabled_vlans; /* List of enabled vlans TLV data */
++  struct list *appvlanfwders; /* Appointed VLAN forwarders */
++  u_int32_t vlanfwdershash;   /* Hash to check change in VLAN forwarders */
++  struct list *inhibit_vlans;	/* VLANs inhibited by foreign AF flags (rare) */
++  time_t inhibit_all;		/* All inhibited by root bridge (common) */
++  struct thread *inhibit_thread;
++};
++
++struct trill_adj_vlans
++{
++  u_int16_t designated;   /* Designated VLAN when adj is DR */
++  u_int8_t forwarder[VLANS_ARRSIZE];  /* VLANs the adj forwards */
++  u_int8_t enabled[VLANS_ARRSIZE]; /* VLANs the adj has enabled */
++  u_int8_t seen[VLANS_ARRSIZE]; /* VLANs we send hellos to adj */
++};
++
++struct trill_vlan_bridge_roots
++{
++    int vlan_start;
++    int vlan_end;
++    int bridge_roots_count;
++    struct ether_addr *bridge_roots;
++};
++
++#endif /* ISIS_VLANS_H */
+diff --git isisd/isisd.c isisd/isisd.c
+index 7c669fc..3ea6fcd 100644
+--- isisd/isisd.c
++++ isisd/isisd.c
+@@ -40,7 +40,6 @@
+ #include "isisd/isis_common.h"
+ #include "isisd/isis_circuit.h"
+ #include "isisd/isis_flags.h"
+-#include "isisd/isisd.h"
+ #include "isisd/isis_dynhn.h"
+ #include "isisd/isis_adjacency.h"
+ #include "isisd/isis_pdu.h"
+@@ -52,6 +51,12 @@
+ #include "isisd/isis_route.h"
+ #include "isisd/isis_zebra.h"
+ #include "isisd/isis_events.h"
++#ifdef HAVE_TRILL
++#include "isisd/isis_vlans.h"
++#include "isisd/isis_trill.h"
++#endif
++#include "isisd/isisd.h"
++#include "isisd/bool.h"
+ 
+ #ifdef TOPOLOGY_GENERATE
+ #include "spgrid.h"
+@@ -64,14 +69,11 @@ extern struct thread_master *master;
+ /*
+  * Prototypes.
+  */
+-void isis_new(unsigned long);
+-struct isis_area *isis_area_create(void);
+ int isis_area_get(struct vty *, const char *);
+ int isis_area_destroy(struct vty *, const char *);
+-int area_net_title(struct vty *, const u_char *);
+-int area_clear_net_title(struct vty *, const u_char *);
++int area_net_title(struct vty *, const char *);
++int area_clear_net_title(struct vty *, const char *);
+ int show_clns_neigh(struct vty *, char);
+-void print_debug(struct vty *, int, int);
+ int isis_config_write(struct vty *);
+ 
+ 
+@@ -100,7 +102,7 @@ isis_new (unsigned long process_id)
+ }
+ 
+ struct isis_area *
+-isis_area_create ()
++isis_area_create (const char *area_tag)
+ {
+   struct isis_area *area;
+ 
+@@ -131,6 +133,9 @@ isis_area_create ()
+   area->area_addrs = list_new ();
+   THREAD_TIMER_ON (master, area->t_tick, lsp_tick, area, 1);
+   flags_initialize (&area->flags);
++#ifdef HAVE_TRILL
++  area->trill = XCALLOC (MTYPE_ISIS_TRILLAREA, sizeof (struct trill_info));
++#endif
+   /*
+    * Default values
+    */
+@@ -152,6 +157,15 @@ isis_area_create ()
+   /* FIXME: Think of a better way... */
+   area->min_bcast_mtu = 1497;
+ 
++  area->area_tag = strdup (area_tag);
++  listnode_add (isis->area_list, area);
++  area->isis = isis;
++
++#ifdef HAVE_TRILL
++  if (isis->trill_active)
++    trill_area_init (area);
++#endif
++
+   return area;
+ }
+ 
+@@ -184,9 +198,17 @@ isis_area_get (struct vty *vty, const char *area_tag)
+       return CMD_SUCCESS;
+     }
+ 
+-  area = isis_area_create ();
+-  area->area_tag = strdup (area_tag);
+-  listnode_add (isis->area_list, area);
++#ifdef HAVE_TRILL
++  if (isis->trill_active && isis->area_list->count != 0)
++    { 
++      vty_out (vty,
++	"TRILL area exists, can only define one IS-IS area for TRILL. %s",
++	VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++#endif
++
++  area = isis_area_create (area_tag);
+ 
+   if (isis->debugs & DEBUG_EVENTS)
+     zlog_debug ("New IS-IS area instance %s", area->area_tag);
+@@ -212,6 +234,12 @@ isis_area_destroy (struct vty *vty, const char *area_tag)
+       return CMD_WARNING;
+     }
+ 
++#ifdef HAVE_TRILL
++  trill_area_free (area);
++  XFREE (MTYPE_ISIS_TRILLAREA, area->trill);
++#endif
++  spftree_area_del (area);
++
+   if (area->circuit_list)
+     {
+       for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit))
+@@ -230,13 +258,14 @@ isis_area_destroy (struct vty *vty, const char *area_tag)
+   THREAD_TIMER_OFF (area->t_lsp_refresh[0]);
+   THREAD_TIMER_OFF (area->t_lsp_refresh[1]);
+ 
++  free (area->area_tag);
+   XFREE (MTYPE_ISIS_AREA, area);
+ 
+   return CMD_SUCCESS;
+ }
+ 
+ int
+-area_net_title (struct vty *vty, const u_char *net_title)
++area_net_title (struct vty *vty, const char *net_title)
+ {
+   struct isis_area *area;
+   struct area_addr *addr;
+@@ -330,7 +359,7 @@ area_net_title (struct vty *vty, const u_char *net_title)
+ }
+ 
+ int
+-area_clear_net_title (struct vty *vty, const u_char *net_title)
++area_clear_net_title (struct vty *vty, const char *net_title)
+ {
+   struct isis_area *area;
+   struct area_addr addr, *addrp = NULL;
+@@ -524,7 +553,10 @@ print_debug (struct vty *vty, int flags, int onoff)
+ 	     VTY_NEWLINE);
+   if (flags & DEBUG_EVENTS)
+     vty_out (vty, "IS-IS Event debugging is %s%s", onoffs, VTY_NEWLINE);
+-
++#ifdef HAVE_TRILL
++  if (flags & DEBUG_TRILL_EVENTS)
++    vty_out (vty, "IS-IS TRILL Event debugging is %s%s", onoffs, VTY_NEWLINE);
++#endif
+ }
+ 
+ DEFUN (show_debugging,
+@@ -606,6 +638,13 @@ config_write_debug (struct vty *vty)
+       vty_out (vty, "debug isis events%s", VTY_NEWLINE);
+       write++;
+     }
++#ifdef HAVE_TRILL
++  if (flags & DEBUG_TRILL_EVENTS)
++    {
++      vty_out (vty, "debug trill events%s", VTY_NEWLINE);
++      write++;
++    }
++#endif
+ 
+   return write;
+ }
+@@ -792,7 +831,6 @@ DEFUN (no_debug_isis_spfevents,
+   return CMD_SUCCESS;
+ }
+ 
+-
+ DEFUN (debug_isis_spfstats,
+        debug_isis_spfstats_cmd,
+        "debug isis spf-statistics ",
+@@ -1045,7 +1083,7 @@ DEFUN (area_passwd,
+ 
+   if (!area)
+     {
+-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
++      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
+       return CMD_WARNING;
+     }
+ 
+@@ -1098,7 +1136,7 @@ DEFUN (no_area_passwd,
+ 
+   if (!area)
+     {
+-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
++      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
+       return CMD_WARNING;
+     }
+ 
+@@ -1120,7 +1158,7 @@ DEFUN (domain_passwd,
+ 
+   if (!area)
+     {
+-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
++      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
+       return CMD_WARNING;
+     }
+ 
+@@ -1173,7 +1211,7 @@ DEFUN (no_domain_passwd,
+ 
+   if (!area)
+     {
+-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
++      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
+       return CMD_WARNING;
+     }
+ 
+@@ -1197,7 +1235,7 @@ DEFUN (is_type,
+ 
+   if (!area)
+     {
+-      vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE);
++      vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE);
+       return CMD_WARNING;
+     }
+ 
+@@ -1207,6 +1245,14 @@ DEFUN (is_type,
+       vty_out (vty, "Unknown IS level %s", VTY_NEWLINE);
+       return CMD_SUCCESS;
+     }
++ 
++#ifdef HAVE_TRILL
++  if (area->isis->trill_active)
++    {
++      vty_out (vty, "Can't set level when TRILL is enabled%s", VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++#endif
+ 
+   isis_event_system_type_change (area, type);
+ 
+@@ -1228,6 +1274,14 @@ DEFUN (no_is_type,
+   area = vty->index;
+   assert (area);
+ 
++#ifdef HAVE_TRILL
++  if (area->isis->trill_active)
++    {
++      vty_out (vty, "Can't reset level when TRILL is enabled%s", VTY_NEWLINE);
++      return CMD_WARNING;
++    }
++#endif
++
+   /*
+    * Put the is-type back to default. Which is level-1-2 on first
+    * circuit for the area level-1 for the rest
+@@ -1894,6 +1948,15 @@ isis_config_write (struct vty *vty)
+       struct isis_area *area;
+       struct listnode *node, *node2;
+ 
++#ifdef HAVE_TRILL
++      /* ISIS - TRILL enabled control flag */
++      if (isis->trill_active)
++        {
++          vty_out (vty, "isis trill%s", VTY_NEWLINE);
++          write++;
++	}
++#endif
++
+       for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
+       {
+ 	/* ISIS - Area name */
+@@ -1943,6 +2006,31 @@ isis_config_write (struct vty *vty)
+ 		write++;
+ 	      }
+ 	  }
++
++#ifdef HAVE_TRILL
++	/* ISIS TRILL configuration options */
++	if (isis->trill_active)
++          {
++	     if (CHECK_FLAG(area->trill->status, TRILL_NICK_SET) &&
++	          (!CHECK_FLAG(area->trill->status, TRILL_AUTONICK)))
++               {
++                 vty_out (vty, " trill nickname %d%s", area->trill->nick.name,
++				 VTY_NEWLINE);
++                 write++;
++               }
++	     if (CHECK_FLAG(area->trill->status, TRILL_PRIORITY_SET))
++               {
++                 vty_out (vty, " trill nickname priority %d%s",
++			 (area->trill->nick.priority & (~CONFIGURED_NICK_PRIORITY)),
++			 VTY_NEWLINE);
++                 write++;
++               }
++
++	     vty_out(vty, " trill instance %s%s", area->trill->name, VTY_NEWLINE);
++	     write++;
++	  }
++#endif
++
+ 	/* ISIS - Lsp generation interval */
+ 	if (area->lsp_gen_interval[0] == area->lsp_gen_interval[1])
+ 	  {
+@@ -2053,7 +2141,7 @@ isis_config_write (struct vty *vty)
+ 		    ISIS_SYS_ID_LEN))
+ 	  {
+ 	    vty_out (vty, " topology base-is %s%s",
+-		     sysid_print (area->topology_baseis), VTY_NEWLINE);
++		     sysid_print ((u_char *)area->topology_baseis), VTY_NEWLINE);
+ 	    write++;
+ 	  }
+ 	if (area->topology_basedynh)
+@@ -2218,9 +2306,4 @@ isis_init ()
+   install_element (VIEW_NODE, &show_isis_generated_topology_cmd);
+   install_element (ENABLE_NODE, &show_isis_generated_topology_cmd);
+ #endif /* TOPOLOGY_GENERATE */
+-
+-  isis_new (0);
+-  isis_circuit_init ();
+-  isis_zebra_init ();
+-  isis_spf_cmds_init ();
+ }
+diff --git isisd/isisd.h isisd/isisd.h
+index 2277f27..945d423 100644
+--- isisd/isisd.h
++++ isisd/isisd.h
+@@ -51,6 +51,9 @@ struct isis
+   u_int32_t debugs;		/* bitmap for debug */
+   time_t uptime;		/* when did we start */
+   struct thread *t_dync_clean;	/* dynamic hostname cache cleanup thread */
++#ifdef HAVE_TRILL
++  int trill_active;		/* TRILL support is active */
++#endif
+ 
+   /* Redistributed external information. */
+   struct route_table *external_info[ZEBRA_ROUTE_MAX + 1];
+@@ -78,6 +81,8 @@ struct isis
+ #endif
+ };
+ 
++extern struct isis *isis;
++
+ struct isis_area
+ {
+   struct isis *isis;				  /* back pointer */
+@@ -95,6 +100,9 @@ struct isis_area
+   struct thread *t_remove_aged;
+   int lsp_regenerate_pending[ISIS_LEVELS];
+   struct thread *t_lsp_refresh[ISIS_LEVELS];
++#ifdef HAVE_TRILL
++  struct trill_info *trill;     /* TRILL IS-IS information */
++#endif
+ 
+   /*
+    * Configurables 
+@@ -128,14 +136,20 @@ struct isis_area
+ 
+ #ifdef TOPOLOGY_GENERATE
+   struct list *topology;
+-  char topology_baseis[ISIS_SYS_ID_LEN];  /* IS for the first IS emulated. */
++  u_char topology_baseis[ISIS_SYS_ID_LEN];  /* IS for the first IS emulated. */
+   char *topology_basedynh;                /* Dynamic hostname base. */
+   char top_params[200];                   /* FIXME: what is reasonable? */
+ #endif /* TOPOLOGY_GENERATE */
+ };
+ 
+ void isis_init (void);
++void isis_new(unsigned long);
++struct isis_area *isis_area_create(const char *);
+ struct isis_area *isis_area_lookup (const char *);
++void print_debug(struct vty *, int, int);
++
++/* Master of threads. */
++extern struct thread_master *master;
+ 
+ #define DEBUG_ADJ_PACKETS                (1<<0)
+ #define DEBUG_CHECKSUM_ERRORS            (1<<1)
+@@ -149,5 +163,8 @@ struct isis_area *isis_area_lookup (const char *);
+ #define DEBUG_RTE_EVENTS                 (1<<9)
+ #define DEBUG_EVENTS                     (1<<10)
+ #define DEBUG_ZEBRA                      (1<<11)
++#ifdef HAVE_TRILL
++#define DEBUG_TRILL_EVENTS		 (1<<12)
++#endif
+ 
+ #endif /* ISISD_H */
+diff --git lib/command.c lib/command.c
+index 270bf0d..2c8d752 100644
+--- lib/command.c
++++ lib/command.c
+@@ -193,8 +193,8 @@ install_node (struct cmd_node *node,
+ static int
+ cmp_node (const void *p, const void *q)
+ {
+-  const struct cmd_element *a = *(struct cmd_element **)p;
+-  const struct cmd_element *b = *(struct cmd_element **)q;
++  const struct cmd_element *a = *(struct cmd_element * const *)p;
++  const struct cmd_element *b = *(struct cmd_element * const *)q;
+ 
+   return strcmp (a->string, b->string);
+ }
+@@ -202,8 +202,8 @@ cmp_node (const void *p, const void *q)
+ static int
+ cmp_desc (const void *p, const void *q)
+ {
+-  const struct desc *a = *(struct desc **)p;
+-  const struct desc *b = *(struct desc **)q;
++  const struct desc *a = *(struct desc * const *)p;
++  const struct desc *b = *(struct desc * const *)q;
+ 
+   return strcmp (a->cmd, b->cmd);
+ }
+@@ -491,8 +491,11 @@ install_element (enum node_type ntype, struct cmd_element *cmd)
+ 
+   vector_set (cnode->cmd_vector, cmd);
+ 
+-  cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc);
+-  cmd->cmdsize = cmd_cmdsize (cmd->strvec);
++  if (cmd->strvec == NULL)
++    {
++      cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc);
++      cmd->cmdsize = cmd_cmdsize (cmd->strvec);
++    }
+ }
+ 
+ static unsigned char itoa64[] =	
+@@ -3497,6 +3500,8 @@ DEFUN (no_banner_motd,
+ void
+ host_config_set (char *filename)
+ {
++  if (host.config != NULL)
++    XFREE (MTYPE_HOST, host.config);
+   host.config = XSTRDUP (MTYPE_HOST, filename);
+ }
+ 
+diff --git lib/command.h lib/command.h
+index a725378..fefa6d3 100644
+--- lib/command.h
++++ lib/command.h
+@@ -302,6 +302,8 @@ struct desc
+ #define OSPF6_DUMP_TYPE_LIST \
+ "(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)"
+ #define ISIS_STR "IS-IS information\n"
++#define TRILL_STR "TRILL IS-IS information\n"
++#define TRILL_NICK_STR "Configure TRILL RBridge nickname information\n"
+ #define AREA_TAG_STR "[area tag]\n"
+ 
+ #define CONF_BACKUP_EXT ".sav"
+@@ -359,5 +361,5 @@ extern void host_config_set (char *);
+ extern void print_version (const char *);
+ 
+ /* struct host global, ick */
+-extern struct host host; 
++extern struct host host;
+ #endif /* _ZEBRA_COMMAND_H */
+diff --git lib/linklist.c lib/linklist.c
+index 11e16a8..a16e9e1 100644
+--- lib/linklist.c
++++ lib/linklist.c
+@@ -65,7 +65,9 @@ void
+ listnode_add (struct list *list, void *val)
+ {
+   struct listnode *node;
+-
++  
++  assert (val != NULL);
++  
+   node = listnode_new ();
+ 
+   node->prev = list->tail;
+@@ -91,7 +93,9 @@ listnode_add_sort (struct list *list, void *val)
+ {
+   struct listnode *n;
+   struct listnode *new;
+-
++  
++  assert (val != NULL);
++  
+   new = listnode_new ();
+   new->data = val;
+ 
+@@ -130,7 +134,9 @@ void
+ listnode_add_after (struct list *list, struct listnode *pp, void *val)
+ {
+   struct listnode *nn;
+-
++  
++  assert (val != NULL);
++  
+   nn = listnode_new ();
+   nn->data = val;
+ 
+@@ -158,6 +164,7 @@ listnode_add_after (struct list *list, struct listnode *pp, void *val)
+ 
+       pp->next = nn;
+     }
++  list->count++;
+ }
+ 
+ 
+@@ -265,7 +272,9 @@ void
+ list_add_node_prev (struct list *list, struct listnode *current, void *val)
+ {
+   struct listnode *node;
+-
++  
++  assert (val != NULL);
++  
+   node = listnode_new ();
+   node->next = current;
+   node->data = val;
+@@ -286,7 +295,9 @@ void
+ list_add_node_next (struct list *list, struct listnode *current, void *val)
+ {
+   struct listnode *node;
+-
++  
++  assert (val != NULL);
++  
+   node = listnode_new ();
+   node->prev = current;
+   node->data = val;
+diff --git lib/memory.c lib/memory.c
+index eb67072..9ed5e10 100644
+--- lib/memory.c
++++ lib/memory.c
+@@ -501,7 +501,7 @@ mtype_memstr (char *buf, size_t len, unsigned long bytes)
+        * Just hacked to make it not warn on 'smaller' machines. 
+        * Static compiler analysis should mean no extra code
+        */
+-      if (bytes & (1 << (sizeof (unsigned long) >= 8 ? 39 : 0)))
++      if (bytes & (1UL << (sizeof (unsigned long) >= 8 ? 39 : 0)))
+         t++;
+       snprintf (buf, len, "%4d TiB", t);
+     }
+diff --git lib/memtypes.c lib/memtypes.c
+index c7028c9..476ad35 100644
+--- lib/memtypes.c
++++ lib/memtypes.c
+@@ -225,20 +225,32 @@ struct memory_list memory_list_ospf6[] =
+ 
+ struct memory_list memory_list_isis[] =
+ {
+-  { MTYPE_ISIS,               "ISIS"				},
+-  { MTYPE_ISIS_TMP,           "ISIS TMP"			},
+-  { MTYPE_ISIS_CIRCUIT,       "ISIS circuit"			},
+-  { MTYPE_ISIS_LSP,           "ISIS LSP"			},
+-  { MTYPE_ISIS_ADJACENCY,     "ISIS adjacency"			},
+-  { MTYPE_ISIS_AREA,          "ISIS area"			},
+-  { MTYPE_ISIS_AREA_ADDR,     "ISIS area address"		},
+-  { MTYPE_ISIS_TLV,           "ISIS TLV"			},
+-  { MTYPE_ISIS_DYNHN,         "ISIS dyn hostname"		},
+-  { MTYPE_ISIS_SPFTREE,       "ISIS SPFtree"			},
+-  { MTYPE_ISIS_VERTEX,        "ISIS vertex"			},
+-  { MTYPE_ISIS_ROUTE_INFO,    "ISIS route info"			},
+-  { MTYPE_ISIS_NEXTHOP,       "ISIS nexthop"			},
+-  { MTYPE_ISIS_NEXTHOP6,      "ISIS nexthop6"			},
++  { MTYPE_ISIS,                  "ISIS"				},
++  { MTYPE_ISIS_TMP,              "ISIS TMP"			},
++  { MTYPE_ISIS_CIRCUIT,          "ISIS circuit"			},
++  { MTYPE_ISIS_LSP,              "ISIS LSP"			},
++  { MTYPE_ISIS_ADJACENCY,        "ISIS adjacency"		},
++  { MTYPE_ISIS_AREA,             "ISIS area"			},
++  { MTYPE_ISIS_AREA_ADDR,        "ISIS area address"		},
++  { MTYPE_ISIS_TLV,              "ISIS TLV"			},
++  { MTYPE_ISIS_DYNHN,            "ISIS dyn hostname"		},
++  { MTYPE_ISIS_SPFTREE,          "ISIS SPFtree"			},
++  { MTYPE_ISIS_VERTEX,           "ISIS vertex"			},
++  { MTYPE_ISIS_ROUTE_INFO,       "ISIS route info"		},
++  { MTYPE_ISIS_NEXTHOP,          "ISIS nexthop"			},
++  { MTYPE_ISIS_NEXTHOP6,         "ISIS nexthop6"		},
++  { MTYPE_ISIS_TRILLAREA,	 "ISIS TRILL area information"  },
++  { MTYPE_ISIS_TRILL_NICKDB_NODE,"ISIS TRILL nickdb node"   	},
++  { MTYPE_ISIS_TRILL_FWDTBL_NODE,"ISIS TRILL forwarding table node"},
++  { MTYPE_ISIS_TRILL_VLANS,	 "ISIS TRILL vlans"},
++  { MTYPE_ISIS_TRILL_ADJVLANS,	 "ISIS TRILL adjacency vlans"},
++  { MTYPE_ISIS_TRILL_VLANBRIDGE_ROOTS, "ISIS TRILL vlan bridge roots"},
++  { MTYPE_ISIS_TRILL_BRIDGE_ROOTIDS, "ISIS TRILL vlan bridge root IDs"},
++  { MTYPE_ISIS_TRILL_VLANSREACHABLE, "ISIS TRILL vlans reachable downstream"},
++  { MTYPE_ISIS_TRILL_VLANFWDERS, "ISIS TRILL VLAN forwarders" },
++  { MTYPE_ISIS_TRILL_ENABLEDVLANS, "ISIS TRILL enabled VLAN maps" },
++  { MTYPE_ISIS_TRILL_VLANSUBTLV, "ISIS TRILL VLAN subTLV construction" },
++  { MTYPE_ISIS_TRILL_INHIB,	 "ISIS TRILL VLAN inhibit entry" },
+   { -1, NULL },
+ };
+ 
+diff --git lib/memtypes.h lib/memtypes.h
+deleted file mode 100644
+index 2d90e80..0000000
+--- lib/memtypes.h
++++ /dev/null
+@@ -1,206 +0,0 @@
+-/* Auto-generated from memtypes.c by gawk. */
+-/* Do not edit! */
+-
+-#ifndef _QUAGGA_MEMTYPES_H
+-#define _QUAGGA_MEMTYPES_H
+-
+-enum
+-{
+-  MTYPE_TMP = 1,
+-  MTYPE_STRVEC,
+-  MTYPE_VECTOR,
+-  MTYPE_VECTOR_INDEX,
+-  MTYPE_LINK_LIST,
+-  MTYPE_LINK_NODE,
+-  MTYPE_THREAD,
+-  MTYPE_THREAD_MASTER,
+-  MTYPE_THREAD_STATS,
+-  MTYPE_THREAD_FUNCNAME,
+-  MTYPE_VTY,
+-  MTYPE_VTY_OUT_BUF,
+-  MTYPE_VTY_HIST,
+-  MTYPE_IF,
+-  MTYPE_CONNECTED,
+-  MTYPE_CONNECTED_LABEL,
+-  MTYPE_BUFFER,
+-  MTYPE_BUFFER_DATA,
+-  MTYPE_STREAM,
+-  MTYPE_STREAM_DATA,
+-  MTYPE_STREAM_FIFO,
+-  MTYPE_PREFIX,
+-  MTYPE_PREFIX_IPV4,
+-  MTYPE_PREFIX_IPV6,
+-  MTYPE_HASH,
+-  MTYPE_HASH_BACKET,
+-  MTYPE_HASH_INDEX,
+-  MTYPE_ROUTE_TABLE,
+-  MTYPE_ROUTE_NODE,
+-  MTYPE_DISTRIBUTE,
+-  MTYPE_DISTRIBUTE_IFNAME,
+-  MTYPE_ACCESS_LIST,
+-  MTYPE_ACCESS_LIST_STR,
+-  MTYPE_ACCESS_FILTER,
+-  MTYPE_PREFIX_LIST,
+-  MTYPE_PREFIX_LIST_ENTRY,
+-  MTYPE_PREFIX_LIST_STR,
+-  MTYPE_ROUTE_MAP,
+-  MTYPE_ROUTE_MAP_NAME,
+-  MTYPE_ROUTE_MAP_INDEX,
+-  MTYPE_ROUTE_MAP_RULE,
+-  MTYPE_ROUTE_MAP_RULE_STR,
+-  MTYPE_ROUTE_MAP_COMPILED,
+-  MTYPE_DESC,
+-  MTYPE_KEY,
+-  MTYPE_KEYCHAIN,
+-  MTYPE_IF_RMAP,
+-  MTYPE_IF_RMAP_NAME,
+-  MTYPE_SOCKUNION,
+-  MTYPE_PRIVS,
+-  MTYPE_ZLOG,
+-  MTYPE_ZCLIENT,
+-  MTYPE_WORK_QUEUE,
+-  MTYPE_WORK_QUEUE_ITEM,
+-  MTYPE_WORK_QUEUE_NAME,
+-  MTYPE_PQUEUE,
+-  MTYPE_PQUEUE_DATA,
+-  MTYPE_HOST,
+-  MTYPE_RTADV_PREFIX,
+-  MTYPE_VRF,
+-  MTYPE_VRF_NAME,
+-  MTYPE_NEXTHOP,
+-  MTYPE_RIB,
+-  MTYPE_RIB_QUEUE,
+-  MTYPE_STATIC_IPV4,
+-  MTYPE_STATIC_IPV6,
+-  MTYPE_BGP,
+-  MTYPE_BGP_PEER,
+-  MTYPE_BGP_PEER_HOST,
+-  MTYPE_PEER_GROUP,
+-  MTYPE_PEER_DESC,
+-  MTYPE_ATTR,
+-  MTYPE_ATTR_EXTRA,
+-  MTYPE_AS_PATH,
+-  MTYPE_AS_SEG,
+-  MTYPE_AS_SEG_DATA,
+-  MTYPE_AS_STR,
+-  MTYPE_BGP_TABLE,
+-  MTYPE_BGP_NODE,
+-  MTYPE_BGP_ROUTE,
+-  MTYPE_BGP_ROUTE_EXTRA,
+-  MTYPE_BGP_STATIC,
+-  MTYPE_BGP_ADVERTISE_ATTR,
+-  MTYPE_BGP_ADVERTISE,
+-  MTYPE_BGP_SYNCHRONISE,
+-  MTYPE_BGP_ADJ_IN,
+-  MTYPE_BGP_ADJ_OUT,
+-  MTYPE_AS_LIST,
+-  MTYPE_AS_FILTER,
+-  MTYPE_AS_FILTER_STR,
+-  MTYPE_COMMUNITY,
+-  MTYPE_COMMUNITY_VAL,
+-  MTYPE_COMMUNITY_STR,
+-  MTYPE_ECOMMUNITY,
+-  MTYPE_ECOMMUNITY_VAL,
+-  MTYPE_ECOMMUNITY_STR,
+-  MTYPE_COMMUNITY_LIST,
+-  MTYPE_COMMUNITY_LIST_NAME,
+-  MTYPE_COMMUNITY_LIST_ENTRY,
+-  MTYPE_COMMUNITY_LIST_CONFIG,
+-  MTYPE_COMMUNITY_LIST_HANDLER,
+-  MTYPE_CLUSTER,
+-  MTYPE_CLUSTER_VAL,
+-  MTYPE_BGP_PROCESS_QUEUE,
+-  MTYPE_BGP_CLEAR_NODE_QUEUE,
+-  MTYPE_TRANSIT,
+-  MTYPE_TRANSIT_VAL,
+-  MTYPE_BGP_DISTANCE,
+-  MTYPE_BGP_NEXTHOP_CACHE,
+-  MTYPE_BGP_CONFED_LIST,
+-  MTYPE_PEER_UPDATE_SOURCE,
+-  MTYPE_BGP_DAMP_INFO,
+-  MTYPE_BGP_DAMP_ARRAY,
+-  MTYPE_BGP_REGEXP,
+-  MTYPE_BGP_AGGREGATE,
+-  MTYPE_RIP,
+-  MTYPE_RIP_INFO,
+-  MTYPE_RIP_INTERFACE,
+-  MTYPE_RIP_PEER,
+-  MTYPE_RIP_OFFSET_LIST,
+-  MTYPE_RIP_DISTANCE,
+-  MTYPE_RIPNG,
+-  MTYPE_RIPNG_ROUTE,
+-  MTYPE_RIPNG_AGGREGATE,
+-  MTYPE_RIPNG_PEER,
+-  MTYPE_RIPNG_OFFSET_LIST,
+-  MTYPE_RIPNG_RTE_DATA,
+-  MTYPE_OSPF_TOP,
+-  MTYPE_OSPF_AREA,
+-  MTYPE_OSPF_AREA_RANGE,
+-  MTYPE_OSPF_NETWORK,
+-  MTYPE_OSPF_NEIGHBOR_STATIC,
+-  MTYPE_OSPF_IF,
+-  MTYPE_OSPF_NEIGHBOR,
+-  MTYPE_OSPF_ROUTE,
+-  MTYPE_OSPF_TMP,
+-  MTYPE_OSPF_LSA,
+-  MTYPE_OSPF_LSA_DATA,
+-  MTYPE_OSPF_LSDB,
+-  MTYPE_OSPF_PACKET,
+-  MTYPE_OSPF_FIFO,
+-  MTYPE_OSPF_VERTEX,
+-  MTYPE_OSPF_VERTEX_PARENT,
+-  MTYPE_OSPF_NEXTHOP,
+-  MTYPE_OSPF_PATH,
+-  MTYPE_OSPF_VL_DATA,
+-  MTYPE_OSPF_CRYPT_KEY,
+-  MTYPE_OSPF_EXTERNAL_INFO,
+-  MTYPE_OSPF_DISTANCE,
+-  MTYPE_OSPF_IF_INFO,
+-  MTYPE_OSPF_IF_PARAMS,
+-  MTYPE_OSPF_MESSAGE,
+-  MTYPE_OSPF6_TOP,
+-  MTYPE_OSPF6_AREA,
+-  MTYPE_OSPF6_IF,
+-  MTYPE_OSPF6_NEIGHBOR,
+-  MTYPE_OSPF6_ROUTE,
+-  MTYPE_OSPF6_PREFIX,
+-  MTYPE_OSPF6_MESSAGE,
+-  MTYPE_OSPF6_LSA,
+-  MTYPE_OSPF6_LSA_SUMMARY,
+-  MTYPE_OSPF6_LSDB,
+-  MTYPE_OSPF6_VERTEX,
+-  MTYPE_OSPF6_SPFTREE,
+-  MTYPE_OSPF6_NEXTHOP,
+-  MTYPE_OSPF6_EXTERNAL_INFO,
+-  MTYPE_OSPF6_OTHER,
+-  MTYPE_ISIS,
+-  MTYPE_ISIS_TMP,
+-  MTYPE_ISIS_CIRCUIT,
+-  MTYPE_ISIS_LSP,
+-  MTYPE_ISIS_ADJACENCY,
+-  MTYPE_ISIS_AREA,
+-  MTYPE_ISIS_AREA_ADDR,
+-  MTYPE_ISIS_TLV,
+-  MTYPE_ISIS_DYNHN,
+-  MTYPE_ISIS_SPFTREE,
+-  MTYPE_ISIS_VERTEX,
+-  MTYPE_ISIS_ROUTE_INFO,
+-  MTYPE_ISIS_NEXTHOP,
+-  MTYPE_ISIS_NEXTHOP6,
+-  MTYPE_VTYSH_CONFIG,
+-  MTYPE_VTYSH_CONFIG_LINE,
+-  MTYPE_MAX,
+-};
+-
+-extern struct memory_list memory_list_lib[];
+-extern struct memory_list memory_list_zebra[];
+-extern struct memory_list memory_list_bgp[];
+-extern struct memory_list memory_list_rip[];
+-extern struct memory_list memory_list_ripng[];
+-extern struct memory_list memory_list_ospf[];
+-extern struct memory_list memory_list_ospf6[];
+-extern struct memory_list memory_list_isis[];
+-extern struct memory_list memory_list_vtysh[];
+-
+-#endif /* _QUAGGA_MEMTYPES_H */
+-
+diff --git lib/privs.c lib/privs.c
+index 69606f5..d290a59 100644
+--- lib/privs.c
++++ lib/privs.c
+@@ -105,6 +105,7 @@ static struct
+   [ZCAP_BIND] =		{ 2, (pvalue_t []) { CAP_NET_BIND_SERVICE,
+                                              CAP_NET_BROADCAST 		}, },
+   [ZCAP_NET_ADMIN] =	{ 1, (pvalue_t []) { CAP_NET_ADMIN		}, },
++  [ZCAP_DL_CONFIG] =	{ 1, (pvalue_t []) { CAP_NET_ADMIN		}, },
+   [ZCAP_NET_RAW] = 	{ 1, (pvalue_t []) { CAP_NET_RAW		}, },
+   [ZCAP_CHROOT] = 	{ 1, (pvalue_t []) { CAP_SYS_CHROOT,		}, },
+   [ZCAP_NICE] = 	{ 1, (pvalue_t []) { CAP_SYS_NICE 		}, },
+@@ -123,6 +124,7 @@ static struct
+ #else
+   [ZCAP_NET_ADMIN] =	{ 1, (pvalue_t []) { PRIV_SYS_NET_CONFIG	}, },
+ #endif
++  [ZCAP_DL_CONFIG] =	{ 1, (pvalue_t []) { PRIV_SYS_DL_CONFIG		}, },
+   [ZCAP_NET_RAW] = 	{ 2, (pvalue_t []) { PRIV_NET_RAWACCESS,
+                                              PRIV_NET_ICMPACCESS 	}, },
+   [ZCAP_CHROOT] = 	{ 1, (pvalue_t []) { PRIV_PROC_CHROOT		}, },
+@@ -137,6 +139,7 @@ static struct
+                                              PRIV_FILE_DAC_READ		}, },
+   [ZCAP_SYS_ADMIN] =	{ 1, (pvalue_t []) { PRIV_SYS_ADMIN		}, },
+   [ZCAP_FOWNER] =	{ 1, (pvalue_t []) { PRIV_FILE_OWNER		}, },
++  [ZCAP_EXEC] =		{ 1, (pvalue_t []) { PRIV_PROC_EXEC		}, },
+ #endif /* HAVE_SOLARIS_CAPABILITIES */
+ };
+ 
+diff --git lib/privs.h lib/privs.h
+index 46d614e..13dbfaf 100644
+--- lib/privs.h
++++ lib/privs.h
+@@ -38,6 +38,8 @@ typedef enum
+   ZCAP_DAC_OVERRIDE,
+   ZCAP_READ_SEARCH,
+   ZCAP_FOWNER,
++  ZCAP_DL_CONFIG,
++  ZCAP_EXEC,
+   ZCAP_MAX
+ } zebra_capabilities_t;
+ 
+diff --git lib/sigevent.c lib/sigevent.c
+index 30e9a3d..2aeb201 100644
+--- lib/sigevent.c
++++ lib/sigevent.c
+@@ -219,6 +219,7 @@ static void
+ trap_default_signals(void)
+ {
+   static const int core_signals[] = {
++#ifndef NO_CORE_SIGNALS
+     SIGQUIT,
+     SIGILL,
+ #ifdef SIGEMT
+@@ -236,6 +237,7 @@ trap_default_signals(void)
+ #ifdef SIGXFSZ
+     SIGXFSZ,
+ #endif
++#endif /* NO_CORE_SIGNALS */
+   };
+   static const int exit_signals[] = {
+     SIGHUP,
+diff --git solaris/Makefile.am solaris/Makefile.am
+index acccbdb..3520bc5 100644
+--- solaris/Makefile.am
++++ solaris/Makefile.am
+@@ -12,7 +12,7 @@ pkg_depends := $(pkg_names:%=depend.%)
+ pkg_packages := $(pkg_names:%=@PACKAGE_TARNAME@-%-$(pkg_name_rev).pkg)
+ pkg_pkginfos := $(pkg_names:%=pkginfo.%.full)
+ pkg_prototypes := $(pkg_names:%=prototype.%)
+-pkg_manifests := quagga.xml
++pkg_manifests := quagga.xml trill.xml
+ 
+ # pkgmk variable substitutions wont grok ${variable} in prototype
+ # file, so we cant let autoconf generate the file sadly
+diff --git solaris/trill.xml.in solaris/trill.xml.in
+new file mode 100644
+index 0000000..f768878
+--- /dev/null
++++ solaris/trill.xml.in
+@@ -0,0 +1,94 @@
++<?xml version="1.0"?>
++<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
++<!--
++	This file is part of Quagga.
++
++	Quagga is free software; you can redistribute it and/or modify it
++	under the terms of the GNU General Public License as published by the
++	Free Software Foundation; either version 2, or (at your option) any
++	later version.
++
++	Quagga is distributed in the hope that it will be useful, but 
++	WITHOUT ANY WARRANTY; without even the implied warranty of
++	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++	General Public License for more details.
++
++	You should have received a copy of the GNU General Public License  
++	along with Quagga; see the file COPYING.  If not, write to the Free
++	Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
++	02111-1307, USA.
++
++	Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
++	Use is subject to license terms.
++-->
++
++<service_bundle type='manifest' name='SUNWtrill-daemons:quagga'>
++
++<service
++	name='network/routing/trill'
++	type='service'
++	version='1'>
++
++	<dependency name='filesystem'
++		grouping='require_all'
++		restart_on='none'
++		type='service'>
++		<service_fmri value='svc:/system/filesystem/local' />
++	</dependency>
++
++	<dependency name='datalink'
++		grouping='require_all'
++		restart_on='none'
++		type='service'>
++		<service_fmri value='svc:/network/datalink-management' />
++	</dependency>
++
++	<exec_method
++		type='method'
++		name='stop'
++		exec=':kill'
++		timeout_seconds='60'>
++	</exec_method>
++
++	<exec_method
++		type='method'
++		name='start'
++		exec='/usr/sbin/trilld -i "" -P 0 %i &amp;'
++		timeout_seconds='60'>
++		<method_context>
++		  <method_credential
++		   user='root' group='root'/>
++ 		</method_context>
++	</exec_method>
++
++	<exec_method
++		type='method'
++		name='refresh'
++		exec=':kill -HUP'
++		timeout_seconds='60'>
++	</exec_method>
++
++	<property_group name='startd'
++		type='framework'>
++		<!-- sub-process core dumps shouldn't restart session -->
++		<propval name='ignore_error'
++		    type='astring' value='core,signal' />
++	</property_group>
++
++	<stability value='Unstable' />
++
++	<template>
++		<common_name>
++			<loctext xml:lang='C'>
++			Quagga: trilld, TRILL bridging protocol daemon.
++			</loctext>
++		</common_name>
++		<documentation>
++			<manpage title='trilld' section='1M'
++				manpath='/usr/share/man' />
++			<doc_link name='quagga.net' 
++				uri='http://www.quagga.net/' />
++		</documentation>
++	</template>
++</service>
++</service_bundle>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/75-privs-basicprivset.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,160 @@
+diff --git lib/privs.c lib/privs.c
+index d290a59..d4dcdf2 100644
+--- lib/privs.c
++++ lib/privs.c
+@@ -2,7 +2,7 @@
+  * Zebra privileges.
+  *
+  * Copyright (C) 2003 Paul Jakma.
+- * Copyright (C) 2005 Sun Microsystems, Inc.
++ * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+  *
+  * This file is part of GNU Zebra.
+  *
+@@ -351,6 +351,26 @@ zprivs_caps_terminate (void)
+  * - http://blogs.sun.com/roller/page/gbrunett?entry=privilege_enabling_set_id_programs1
+  */
+ 
++static pset_t *
++zprivs_caps_minimal ()
++{
++  pset_t *minimal;
++
++  if ((minimal = priv_str_to_set("basic", ",", NULL)) == NULL)
++    {
++      fprintf (stderr, "%s: couldn't get basic set!\n", __func__);
++      exit (1);
++    }
++
++   /* create a minimal privilege set from the basic set */
++  (void) priv_delset(minimal, PRIV_PROC_EXEC);
++  (void) priv_delset(minimal, PRIV_PROC_INFO);
++  (void) priv_delset(minimal, PRIV_PROC_SESSION);
++  (void) priv_delset(minimal, PRIV_FILE_LINK_ANY);
++
++  return  minimal;
++}
++
+ /* convert zebras privileges to system capabilities */
+ static pset_t *
+ zcaps2sys (zebra_capabilities_t *zcaps, int num)
+@@ -379,26 +399,34 @@ zcaps2sys (zebra_capabilities_t *zcaps, int num)
+ int 
+ zprivs_change_caps (zebra_privs_ops_t op)
+ {
++  pset_t *privset;
+   
+   /* should be no possibility of being called without valid caps */
+   assert (zprivs_state.syscaps_p);
+   if (!zprivs_state.syscaps_p)
+     {
++      fprintf (stderr, "%s: Eek, missing privileged caps!", __func__);
++      exit (1);
++    }
++
++  assert (zprivs_state.caps);
++  if (!zprivs_state.caps)
++    {
+       fprintf (stderr, "%s: Eek, missing caps!", __func__);
+       exit (1);
+     }
+-  
+-  /* to raise: copy original permitted into our working effective set
+-   * to lower: just clear the working effective set
++
++  /* to raise: copy original permitted as our working effective set
++   * to lower: copy regular effective set stored in zprivs_state.caps
+    */
+   if (op == ZPRIVS_RAISE)
+-    priv_copyset (zprivs_state.syscaps_p, zprivs_state.caps);
++    privset = zprivs_state.syscaps_p;
+   else if (op == ZPRIVS_LOWER)
+-    priv_emptyset (zprivs_state.caps);
++    privset = zprivs_state.caps;
+   else
+     return -1;
+   
+-  if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0)
++  if (setppriv (PRIV_SET, PRIV_EFFECTIVE, privset) != 0)
+     return -1;
+   
+   return 0;
+@@ -426,15 +454,15 @@ zprivs_state_caps (void)
+     }
+   else
+     {
+-      if (priv_isemptyset (effective) == B_TRUE)
++      if (priv_isequalset (effective, zprivs_state.syscaps_p))
++        result = ZPRIVS_RAISED;
++      else if (priv_isequalset (effective, zprivs_state.caps))
+         result = ZPRIVS_LOWERED;
+       else
+-        result = ZPRIVS_RAISED;
++        result = ZPRIVS_UNKNOWN;
+     }
+   
+-  if (effective)
+-    priv_freeset (effective);
+-  
++  priv_freeset (effective);
+   return result;
+ }
+ 
+@@ -442,7 +470,7 @@ static void
+ zprivs_caps_init (struct zebra_privs_t *zprivs)
+ {
+   pset_t *basic;
+-  pset_t *empty;
++  pset_t *minimal;
+   
+   /* the specified sets */
+   zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
+@@ -470,14 +498,6 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
+   priv_union (basic, zprivs_state.syscaps_p);
+   priv_freeset (basic);
+   
+-  /* we need an empty set for 'effective', potentially for inheritable too */
+-  if ( (empty = priv_allocset()) == NULL)
+-    {
+-      fprintf (stderr, "%s: couldn't get empty set!\n", __func__);
+-      exit (1);
+-    }
+-  priv_emptyset (empty);
+-  
+   /* Hey kernel, we know about privileges! 
+    * this isn't strictly required, use of setppriv should have same effect
+    */
+@@ -520,16 +540,19 @@ zprivs_caps_init (struct zebra_privs_t *zprivs)
+       exit (1);
+     }
+ 
+-  /* now clear the effective set and we're ready to go */
+-  if (setppriv (PRIV_SET, PRIV_EFFECTIVE, empty))
++  /* we need a minimal basic set for 'effective', potentially for inheritable too */
++  minimal = zprivs_caps_minimal();
++
++  /* now set the effective set with a subset of basic privileges */
++  if (setppriv (PRIV_SET, PRIV_EFFECTIVE, minimal))
+     {
+       fprintf (stderr, "%s: error setting effective set!, %s\n", __func__,
+                safe_strerror (errno) );
+       exit (1);
+     }
+   
+-  /* we'll use this as our working-storage privset */
+-  zprivs_state.caps = empty;
++  /* we'll use the minimal set as our working-storage privset */
++  zprivs_state.caps = minimal;
+   
+   /* set methods for the caller to use */
+   zprivs->change = zprivs_change_caps;
+@@ -541,8 +564,7 @@ zprivs_caps_terminate (void)
+ {
+   assert (zprivs_state.caps);
+   
+-  /* clear all capabilities */
+-  priv_emptyset (zprivs_state.caps);
++  /* clear all capabilities by using working-storage privset */
+   setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps);
+   setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.caps);
+   setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.caps);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/80-ripngd-getopt.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,19 @@
+*** ripngd/ripng_main.c
+--- ripngd/ripng_main.c
+***************
+*** 207,213 ****
+      {
+        int opt;
+  
+!       opt = getopt_long (argc, argv, "dlf:i:hA:P:u:g:vC", longopts, 0);
+      
+        if (opt == EOF)
+  	break;
+--- 207,213 ----
+      {
+        int opt;
+  
+!       opt = getopt_long (argc, argv, "dlf:i:hA:P:ru:g:vC", longopts, 0);
+      
+        if (opt == EOF)
+  	break;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/85-remove-fwding-dependency.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,108 @@
+--- solaris/quagga.xml.in
++++ solaris/quagga.xml.in
+@@ -170,14 +170,6 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 
+-	<dependency
+-		name='ipv4-forwarding'
+-		grouping='optional_all'
+-		restart_on='refresh'
+-		type='service'>
+-		<service_fmri value='svc:/network/ipv4-forwarding' />
+-	</dependency>
+-	
+ 	<!-- do not not run unless routing-setup has run -->
+ 	<dependency
+ 		name='network_routing_setup'
+@@ -299,14 +291,6 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 	
+-	<dependency
+-		name='ipv6-forwarding'
+-		grouping='optional_all'
+-		restart_on='refresh'
+-		type='service'>
+-		<service_fmri value='svc:/network/ipv6-forwarding' />
+-	</dependency>
+-
+ 	<!-- do not not run unless routing-setup has run -->
+ 	<dependency
+ 		name='network_routing_setup'
+@@ -428,14 +412,6 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 	
+-	<dependency
+-		name='ipv4-forwarding'
+-		grouping='optional_all'
+-		restart_on='refresh'
+-		type='service'>
+-		<service_fmri value='svc:/network/ipv4-forwarding' />
+-	</dependency>
+-
+ 	<!-- do not not run unless routing-setup has run -->
+ 	<dependency
+ 		name='network_routing_setup'
+@@ -557,14 +533,6 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 	
+-	<dependency
+-		name='ipv6-forwarding'
+-		grouping='optional_all'
+-		restart_on='refresh'
+-		type='service'>
+-		<service_fmri value='svc:/network/ipv6-forwarding' />
+-	</dependency>
+-
+ 	<!-- do not not run unless routing-setup has run -->
+ 	<dependency
+ 		name='network_routing_setup'
+@@ -684,22 +652,6 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 	
+-	<dependency
+-		name='ipv6-forwarding'
+-		grouping='optional_all'
+-		restart_on='refresh'
+-		type='service'>
+-		<service_fmri value='svc:/network/ipv6-forwarding' />
+-	</dependency>
+-
+-	<dependency
+-		name='ipv4-forwarding'
+-		grouping='optional_all'
+-		restart_on='refresh'
+-		type='service'>
+-		<service_fmri value='svc:/network/ipv4-forwarding' />
+-	</dependency>
+-	
+ 	<!-- do not not run unless routing-setup has run -->
+ 	<dependency
+ 		name='network_routing_setup'
+@@ -834,22 +786,6 @@
+ 			value='svc:/system/filesystem/usr:default' />
+ 	</dependency>
+ 
+-	<dependency
+-		name='ipv6-forwarding'
+-		grouping='optional_all'
+-		restart_on='refresh'
+-		type='service'>
+-		<service_fmri value='svc:/network/ipv6-forwarding' />
+-	</dependency>
+-	
+-	<dependency
+-		name='ipv4-forwarding'
+-		grouping='optional_all'
+-		restart_on='refresh'
+-		type='service'>
+-		<service_fmri value='svc:/network/ipv4-forwarding' />
+-	</dependency>
+-
+ 	<!-- do not not run unless routing-setup has run -->
+ 	<dependency
+ 		name='network_routing_setup'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/87-ospfd-apiserver.patch	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,24 @@
+--- solaris/quagga.init.in
++++ solaris/quagga.init.in
+@@ -73,6 +73,9 @@
+ 		args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI no_kernel -n true`"
+ 		args="${args} `get_daemon_option_from_property $SMF_FMRI bgp_port p 179`"
+ 		;;
++	ospfd)
++		args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI apiserver -a true`"
++		;;
+ 	esac
+ 	echo ${args}
+ }
+--- solaris/quagga.xml.in
++++ solaris/quagga.xml.in
+@@ -491,6 +491,9 @@
+ 		<!-- The pidfile to use, if not the default of
+ 		     @quagga_statedir@ -->
+ 		<propval name='pid_file' type='astring' value='' />
++
++		<!-- Options specific to ospfd -->
++		<propval name='apiserver' type='boolean' value='false' />
+ 	</property_group>
+ 
+ 	<property_group name='general' type='framework'>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/patches/90-fix-manpages	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,189 @@
+--- doc/bgpd.8.orig	Sat Jul  2 05:40:07 2011
++++ doc/bgpd.8	Sat Jul  2 06:19:53 2011
+@@ -1,4 +1,4 @@
+-.TH BGPD 8 "25 November 2004" "Quagga BGPD daemon" "Version 0.97.3"
++.TH BGPD 1M "25 November 2004" "Quagga BGPD daemon" "Version 0.97.3"
+ .SH NAME
+ bgpd \- a BGPv4, BGPv4\+, BGPv4\- routing engine for use with Quagga routing
+ software
+@@ -101,12 +101,12 @@
+ file, or through syslog to the system logs. \fBbgpd\fR supports many
+ debugging options, see the Info file, or the source for details.
+ .SH "SEE ALSO"
+-.BR ripd (8),
+-.BR ripngd (8),
+-.BR ospfd (8),
+-.BR ospf6d (8),
+-.BR isisd (8),
+-.BR zebra (8),
++.BR ripd (1m),
++.BR ripngd (1m),
++.BR ospfd (1m),
++.BR ospf6d (1m),
++.BR isisd (1m),
++.BR zebra (1m),
+ .BR vtysh (1)
+ .SH BUGS
+ .B bgpd
+--- doc/isisd.8.orig	Sat Jul  2 05:40:17 2011
++++ doc/isisd.8	Sat Jul  2 06:20:18 2011
+@@ -1,4 +1,4 @@
+-.TH IS-IS 8 "25 November 2004" "Quagga IS-IS daemon" "Version 0.97.3"
++.TH IS-IS 1M "25 November 2004" "Quagga IS-IS daemon" "Version 0.97.3"
+ .SH NAME
+ isisd \- an IS-IS routing engine for use with Quagga routing software.
+ .SH SYNOPSIS
+@@ -90,12 +90,12 @@
+ file, or through syslog to the system logs. \fBisisd\fR supports many
+ debugging options, see the Info file, or the source for details.
+ .SH "SEE ALSO"
+-.BR bgpd (8),
+-.BR ripd (8),
+-.BR ripngd (8),
+-.BR ospfd (8),
+-.BR ospf6d (8),
+-.BR zebra (8),
++.BR bgpd (1m),
++.BR ripd (1m),
++.BR ripngd (1m),
++.BR ospfd (1m),
++.BR ospf6d (1m),
++.BR zebra (1m),
+ .BR vtysh (1)
+ .SH BUGS
+ \fBisisd\fR is ALPHA quality at the moment and hasn't any way ready for
+--- doc/ospf6d.8.orig	Sat Jul  2 05:40:26 2011
++++ doc/ospf6d.8	Sat Jul  2 06:20:43 2011
+@@ -1,4 +1,4 @@
+-.TH OSPF6D 8 "25 November 2004" "Quagga OSPFv3 daemon" "Version 0.97.3"
++.TH OSPF6D 1M "25 November 2004" "Quagga OSPFv3 daemon" "Version 0.97.3"
+ .SH NAME
+ ospf6d \- an OSPFv3 routing engine for use with Quagga routing software.
+ .SH SYNOPSIS
+@@ -91,12 +91,12 @@
+ file, or through syslog to the system logs. \fBospf6d\fR supports many
+ debugging options, see the Info file, or the source for details.
+ .SH "SEE ALSO"
+-.BR bgpd (8),
+-.BR ripd (8),
+-.BR ripngd (8),
+-.BR ospfd (8),
+-.BR isisd (8),
+-.BR zebra (8),
++.BR bgpd (1m),
++.BR ripd (1m),
++.BR ripngd (1m),
++.BR ospfd (1m),
++.BR isisd (1m),
++.BR zebra (1m),
+ .BR vtysh (1)
+ .SH BUGS
+ .B ospf6d
+--- doc/ospfd.8.orig	Sat Jul  2 05:40:33 2011
++++ doc/ospfd.8	Sat Jul  2 06:21:02 2011
+@@ -1,4 +1,4 @@
+-.TH OSPFD 8 "25 November 2004" "Quagga OSPFv2 daemon" "Version 0.97.3"
++.TH OSPFD 1M "25 November 2004" "Quagga OSPFv2 daemon" "Version 0.97.3"
+ .SH NAME
+ ospfd \- an OSPFv2 routing engine for use with Quagga routing software.
+ .SH SYNOPSIS
+@@ -96,12 +96,12 @@
+ file, or through syslog to the system logs. \fBospfd\fR supports many
+ debugging options, see the Info file, or the source for details.
+ .SH "SEE ALSO"
+-.BR bgpd (8),
+-.BR ripd (8),
+-.BR ripngd (8),
+-.BR ospf6d (8),
+-.BR isisd (8),
+-.BR zebra (8),
++.BR bgpd (1m),
++.BR ripd (1m),
++.BR ripngd (1m),
++.BR ospf6d (1m),
++.BR isisd (1m),
++.BR zebra (1m),
+ .BR vtysh (1)
+ .SH BUGS
+ .B ospfd
+--- doc/ripd.8.orig	Sat Jul  2 05:40:40 2011
++++ doc/ripd.8	Sat Jul  2 06:21:39 2011
+@@ -1,4 +1,4 @@
+-.TH RIPD 8 "25 November 2004" "Quagga RIP daemon" "Version 0.97.3"
++.TH RIPD 1M "25 November 2004" "Quagga RIP daemon" "Version 0.97.3"
+ .SH NAME
+ ripd \- a RIP routing engine for use with Quagga routing software.
+ .SH SYNOPSIS
+@@ -94,12 +94,12 @@
+ file, or through syslog to the system logs. \fBripd\fR supports many
+ debugging options, see the Info file, or the source for details.
+ .SH "SEE ALSO"
+-.BR bgpd (8),
+-.BR ripngd (8),
+-.BR ospfd (8),
+-.BR ospf6d (8),
+-.BR isisd (8),
+-.BR zebra (8),
++.BR bgpd (1m),
++.BR ripngd (1m),
++.BR ospfd (1m),
++.BR ospf6d (1m),
++.BR isisd (1m),
++.BR zebra (1m),
+ .BR vtysh (1)
+ .SH BUGS
+ .B ripd
+--- doc/ripngd.8.orig	Sat Jul  2 05:40:49 2011
++++ doc/ripngd.8	Sat Jul  2 06:21:59 2011
+@@ -1,4 +1,4 @@
+-.TH RIPNGD 8 "25 November 2004" "Quagga RIPNG daemon" "Version 0.97.3"
++.TH RIPNGD 1M "25 November 2004" "Quagga RIPNG daemon" "Version 0.97.3"
+ .SH NAME
+ ripngd \- a RIPNG routing engine for use with Quagga routing software.
+ .SH SYNOPSIS
+@@ -97,12 +97,12 @@
+ file, or through syslog to the system logs. \fBripngd\fR supports many
+ debugging options, see the Info file, or the source for details.
+ .SH "SEE ALSO"
+-.BR bgpd (8),
+-.BR ripd (8),
+-.BR ospfd (8),
+-.BR ospf6d (8),
+-.BR isisd (8),
+-.BR zebra (8),
++.BR bgpd (1m),
++.BR ripd (1m),
++.BR ospfd (1m),
++.BR ospf6d (1m),
++.BR isisd (1m),
++.BR zebra (1m),
+ .BR vtysh (1)
+ .SH BUGS
+ .B ripngd
+--- doc/zebra.8.orig	Sat Jul  2 05:40:55 2011
++++ doc/zebra.8	Sat Jul  2 06:22:13 2011
+@@ -1,4 +1,4 @@
+-.TH ZEBRA 8 "25 November 2004" "Zebra daemon" "Version 0.97.3"
++.TH ZEBRA 1M "25 November 2004" "Zebra daemon" "Version 0.97.3"
+ .SH NAME
+ zebra \- a routing manager for use with associated Quagga components.
+ .SH SYNOPSIS
+@@ -116,12 +116,12 @@
+ file, or through syslog to the system logs. \fBzebra\fR supports many
+ debugging options, see the Info file, or the source for details.
+ .SH "SEE ALSO"
+-.BR bgpd (8),
+-.BR ripd (8),
+-.BR ripngd (8),
+-.BR ospfd (8),
+-.BR ospf6d (8),
+-.BR isisd (8),
++.BR bgpd (1m),
++.BR ripd (1m),
++.BR ripngd (1m),
++.BR ospfd (1m),
++.BR ospf6d (1m),
++.BR isisd (1m),
+ .BR vtysh (1)
+ .SH BUGS
+ .B zebra
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/quagga.license	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,47 @@
+Oracle elects to use only the GNU Lesser General Public License version
+2.1 (LGPL)/GNU General Public License version 2 (GPL) for any software
+where a choice of LGPL/GPL license versions are made available with the
+language indicating that LGPLv2.1/GPLv2 or any later version may be
+used, or where a choice of which version of the LGPL/GPL is applied is
+unspecified.  Unless specifically stated otherwise, where a choice
+exists between another license and either the GPL or the LGPL, Oracle
+chooses the other license.
+====================================================================
+
+	Copyright (C) 1985-2005 Free Software Foundation, Inc.
+	Copyright (C) 1996-2001 Kunihiro Ishiguro <[email protected]>
+	Copyright (C) 1997-2000 Toshiaki Takada
+	Copyright (C) 1999,2000 Alex Zinin
+	Copyright (C) 1999 Michael Handler
+	Copyright (C) 1999-2003 Yasuhiro Ohara
+	Copyright (C) 2000,2001 IP Infusion Inc.
+	Copyright (C) 2000 John W. Fraizer III <[email protected]>
+	Copyright (C) 2000 Vladimir B. Grebenschikov <[email protected]>
+	Copyright (C) 1994 X Consortium
+	Copyright (C) 1996 Bob Jenkins ([email protected])
+	Copyright (C) 2000  Robert Olsson.
+	Copyright (C) 2001 KDD R&D Laboratories, Inc.
+	Copyright (c) 2001-2002 Sampo Saaristo,
+	Copyright (C) 2001,2002,2003 Ralph Keller
+	Copyright (C) 2002 6WIND
+	Copyright (C) 2003 David S. Miller ([email protected])
+	Copyright (c) 2003 Paul Jakma
+	Copyright (C) 2004  Andrew J. Schorr
+	Copyright (C) 2004 James R. Leu
+	Copyright 2001,2003,2005 Sun Microsystems, Inc. All rights reserved.
+	Copyright IBM Corporation 1987
+
+	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.  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/quagga/quagga.p5m	Mon Jul 18 12:08:25 2011 -0700
@@ -0,0 +1,158 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+<transform file path=(.+)/man/man1m/(.+).1m$ -> set action.hash %<\1>/man/man8/%<\2>.8 >
+<transform file -> edit path man/man8 man/man1m >
+<transform file path=usr.*/man/.+ -> default mangler.man.stability volatile>
+set name=pkg.fmri \
+    value=pkg:/system/network/routing/quagga@$(IPS_COMPONENT_VERSION),$(BUILD_VERSION)
+set name=pkg.description \
+    value="Quagga is a routing software suite, providing implementations of OSPFv2, OSPFv3, RIP v1 and v2, RIPng, ISIS and BGP-4"
+set name=pkg.summary value="Quagga - Quagga Routing Software"
+set name=info.classification \
+    value="org.opensolaris.category.2008:System/Administration and Configuration"
+set name=info.source_url value=$(COMPONENT_ARCHIVE_URL)
+set name=info.upstream_url value=http://www.quagga.net/
+set name=opensolaris.arc_url \
+    value=http://arc.opensolaris.org/caselog/PSARC/2005/571
+set name=org.opensolaris.consolidation value=$(CONSOLIDATION)
+dir path=etc
+dir path=etc/quagga
+dir path=etc/security
+dir path=etc/security/exec_attr.d
+dir path=lib
+dir path=lib/svc
+dir path=lib/svc/manifest
+dir path=lib/svc/manifest/network
+dir path=lib/svc/manifest/network/routing
+dir path=lib/svc/method
+dir path=usr
+dir path=usr/include
+dir path=usr/include/quagga
+dir path=usr/include/quagga/ospfapi
+dir path=usr/include/quagga/ospfd
+dir path=usr/lib
+dir path=usr/sbin
+dir path=usr/share
+dir path=usr/share/info
+dir path=usr/share/man
+dir path=usr/share/man/man1m
+file Solaris/README.Solaris path=etc/quagga/README.Solaris
+file path=etc/quagga/bgpd.conf.sample
+file path=etc/quagga/bgpd.conf.sample2
+file path=etc/quagga/isisd.conf.sample
+file path=etc/quagga/ospf6d.conf.sample
+file Solaris/ospfd.HA.conf.sample path=etc/quagga/ospfd.HA.conf.sample
+file path=etc/quagga/ospfd.conf.sample
+file path=etc/quagga/ripd.conf.sample
+file path=etc/quagga/ripngd.conf.sample
+file path=etc/quagga/zebra.conf.sample
+file Solaris/exec_attr path=etc/security/exec_attr.d/quagga
+file $(MACH32)/solaris/quagga.xml \
+    path=lib/svc/manifest/network/routing/quagga.xml
+file $(MACH32)/solaris/trill.xml \
+    path=lib/svc/manifest/network/routing/trill.xml
+file $(MACH32)/solaris/quagga.init path=lib/svc/method/quagga
+file path=usr/include/quagga/buffer.h
+file path=usr/include/quagga/checksum.h
+file path=usr/include/quagga/command.h
+file path=usr/include/quagga/distribute.h
+file path=usr/include/quagga/filter.h
+file path=usr/include/quagga/getopt.h
+file path=usr/include/quagga/hash.h
+file path=usr/include/quagga/if.h
+file path=usr/include/quagga/if_rmap.h
+file path=usr/include/quagga/jhash.h
+file path=usr/include/quagga/keychain.h
+file path=usr/include/quagga/linklist.h
+file path=usr/include/quagga/log.h
+file path=usr/include/quagga/md5.h
+file path=usr/include/quagga/memory.h
+file path=usr/include/quagga/memtypes.h
+file path=usr/include/quagga/network.h
+file path=usr/include/quagga/ospfapi/ospf_apiclient.h
+file path=usr/include/quagga/ospfd/ospf_api.h
+file path=usr/include/quagga/ospfd/ospf_asbr.h
+file path=usr/include/quagga/ospfd/ospf_dump.h
+file path=usr/include/quagga/ospfd/ospf_ism.h
+file path=usr/include/quagga/ospfd/ospf_lsa.h
+file path=usr/include/quagga/ospfd/ospf_lsdb.h
+file path=usr/include/quagga/ospfd/ospf_nsm.h
+file path=usr/include/quagga/ospfd/ospf_opaque.h
+file path=usr/include/quagga/ospfd/ospfd.h
+file path=usr/include/quagga/plist.h
+file path=usr/include/quagga/pqueue.h
+file path=usr/include/quagga/prefix.h
+file path=usr/include/quagga/privs.h
+file path=usr/include/quagga/route_types.h
+file path=usr/include/quagga/routemap.h
+file path=usr/include/quagga/sigevent.h
+file path=usr/include/quagga/smux.h
+file path=usr/include/quagga/sockopt.h
+file path=usr/include/quagga/sockunion.h
+file path=usr/include/quagga/str.h
+file path=usr/include/quagga/stream.h
+file path=usr/include/quagga/table.h
+file path=usr/include/quagga/thread.h
+file path=usr/include/quagga/vector.h
+file path=usr/include/quagga/version.h
+file path=usr/include/quagga/vty.h
+file path=usr/include/quagga/workqueue.h
+file path=usr/include/quagga/zassert.h
+file path=usr/include/quagga/zclient.h
+file path=usr/include/quagga/zebra.h
+file path=usr/lib/libospf.so.0.0.0
+file path=usr/lib/libospfapiclient.so.0.0.0
+file path=usr/lib/libzebra.so.0.0.0
+file path=usr/sbin/bgpd
+file path=usr/sbin/isisd
+file path=usr/sbin/ospf6d
+file path=usr/sbin/ospfd
+file Solaris/quaggaadm path=usr/sbin/quaggaadm
+file path=usr/sbin/ripd
+file path=usr/sbin/ripngd
+file path=usr/sbin/trilld
+file path=usr/sbin/zebra
+file path=usr/share/info/quagga.info
+file path=usr/share/info/quagga.info-1
+file path=usr/share/man/man1m/bgpd.1m
+file path=usr/share/man/man1m/isisd.1m
+file path=usr/share/man/man1m/ospf6d.1m
+file path=usr/share/man/man1m/ospfd.1m
+file path=usr/share/man/man1m/ripd.1m
+file path=usr/share/man/man1m/ripngd.1m
+file path=usr/share/man/man1m/zebra.1m
+file Solaris/quagga.1m path=usr/share/man/man8/quagga.1m
+file Solaris/quaggaadm.1m path=usr/share/man/man8/quaggaadm.1m
+legacy pkg=SUNWquaggar desc="Quagga Routing Software (root) 0.99.8" \
+    name="Quagga - Quagga Routing Software (root)"
+legacy pkg=SUNWquaggau desc="Quagga Routing Software (usr) 0.99.8" \
+    name="Quagga - Quagga Routing Software (root)"
+license quagga.license license=GPLv2
+link path=usr/lib/libospf.so target=libospf.so.0.0.0
+link path=usr/lib/libospf.so.0 target=libospf.so.0.0.0
+link path=usr/lib/libospfapiclient.so target=libospfapiclient.so.0.0.0
+link path=usr/lib/libospfapiclient.so.0 target=libospfapiclient.so.0.0.0
+link path=usr/lib/libzebra.so target=libzebra.so.0.0.0
+link path=usr/lib/libzebra.so.0 target=libzebra.so.0.0.0
+link path=usr/sbin/zebraadm target=quaggaadm