# HG changeset patch # User Brian Utterback # Date 1311016105 25200 # Node ID 7c10b5cba79bcc87caad4af609e23e2660283b27 # Parent d3ce52a8aecd56f75aadecb58b552ac2ccd52702 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 diff -r d3ce52a8aecd -r 7c10b5cba79b components/meta-packages/history/SUNWquagga.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:/SUNWquagga@0.99.8,5.11-0.133 +set name=pkg.renamed value=true + +set name=org.opensolaris.consolidation value=$(CONSOLIDATION) + +depend fmri=system/network/routing/quagga@0.99.8-0.133 type=require diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/Makefile --- /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 diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/Solaris/README.Solaris --- /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="" -e ipv4-routing -u + + or + + # svcadm enable + + 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 "). 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 + +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: 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 -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 mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +lo0:1: flags=1000849 mtu 8232 index 1 + inet 172.16.3.91 netmask ffffffff +hme1: flags=1100843 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 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: + #configure terminal + (config)# interface + (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: + + (config-if)# write file + +For further details of the above commands, please see the Quagga +documentation: + + http://www.quagga.net/docs.php diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/Solaris/exec_attr --- /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 diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/Solaris/ospfd.HA.conf.sample --- /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 +! diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/Solaris/quagga.1m --- /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 , or the Info file for an accurate list of authors. diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/Solaris/quaggaadm --- /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 diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/Solaris/quaggaadm.1m --- /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) diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/10-sunw-smf.patch --- /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 "; +- else +- echo "Usage: $0 "; ++ 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 "; + echo "The --pid_file argument is implied"; + echo "This help message: $0 "; + } +@@ -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" + --> + +- ++ ++ + +- ++ ++ + + + + + +- +- ++ +- ++ restart_on='refresh' ++ type='service'> ++ + +- ++ + + + ++ user='root' group='root'/> + + + +@@ -84,11 +86,48 @@ + + +- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + ++ + +- ++ value='solaris.smf.value.routing' /> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -95,6 +134,8 @@ + + ++ + + + + +- ++ + + + + + +- + +- +- +- +- +- ++ +- ++ restart_on='refresh' ++ type='service'> ++ + + ++ ++ ++ ++ ++ + + + ++ user='root' group='root'/> + + + +@@ -179,10 +221,42 @@ + type='astring' value='core,signal' /> + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + +- ++ value='solaris.smf.value.routing' /> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -189,6 +263,8 @@ + + ++ + + + + +- ++ + + + + + +- + +- +- ++ ++ ++ + + +- ++ +- ++ type='service'> ++ + +- ++ + + + ++ user='root' group='root'/> + + + +@@ -273,10 +350,42 @@ + type='astring' value='core,signal' /> + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + +- ++ value='solaris.smf.value.routing' /> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -283,6 +392,8 @@ + + ++ + + + + +- ++ + + + + + +- + +- +- ++ ++ ++ + + +- ++ +- ++ type='service'> ++ + +- ++ + + + ++ user='root' group='root'/> + + + ++ + ++ timeout_seconds='600'> + + + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + +- ++ value='solaris.smf.value.routing' /> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -377,6 +521,8 @@ + + ++ + + + + +- ++ + + + + + +- + +- +- ++ ++ ++ + + +- ++ +- ++ type='service'> ++ + +- ++ + + + ++ user='root' group='root'/> + + + +@@ -461,10 +608,39 @@ + type='astring' value='core,signal' /> + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + +- ++ value='solaris.smf.value.routing' /> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -471,12 +647,14 @@ + + ++ + + + + +- ++ + + + +@@ -506,40 +684,48 @@ + value='svc:/system/filesystem/usr:default' /> + + +- ++ ++ ++ ++ + +- +- ++ ++ ++ + + +- ++ +- ++ type='service'> ++ + +- ++ + + + ++ user='root' group='root'/> + + + +@@ -557,10 +743,55 @@ + type='astring' value='core,signal' /> + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + +- ++ value='solaris.smf.value.routing' /> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -567,6 +798,8 @@ + + ++ + + + + +- ++ + + diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/15-privs-ipinst.patch --- /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 }, }, diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/20-privs-bgpd.patch --- /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 = diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/25-isisd-dlpi.patch --- /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 ++ ++ * configure.ac: Added support for separate link-layer access ++ mechanisms in isisd. ++ + 2007-07-27 Paul Jakma + + * 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 ++ ++ * 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 ++ ++ * 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 + + * 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 + #include ++#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 ++#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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 + #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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 +-#ifdef GNU_LINUX +-#include /* the L2 protocols */ +-#else +-#include +-#include +-#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 +-#else +-#include +-#include +-#include +-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 ++#include /* the L2 protocols */ ++#include ++ ++#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 + { diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/30-ospfd-nssa-asbr.patch --- /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 ++ ++ * 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 + + * 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)) diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/35-ospfd-spf-sort.patch --- /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 +Date: Mon Aug 6 18:52:45 2007 +0000 + + [ospfd] Fix bad SPF calculation on some topologies - incorrect sorting + + 2007-08-07 Atis Elsts + + * 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 ++ ++ * 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 + + * 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 */ diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/40-bgp-capab-cleanup.patch --- /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 +Date: Mon Aug 6 15:21:45 2007 +0000 + + [bgpd] cleanup, compact and consolidate capability parsing code + + 2007-07-26 Paul Jakma + + * (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; + } diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/45-bgpd-capab-typo.patch --- /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 +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 + + * 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; + diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/50-bgpd-nosub.patch --- /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 +Date: Thu Aug 23 23:22:02 2007 +0000 + + [bgpd] Pass NOSUB to regexec + + 2007-08-23 Paul Jakma + + * 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); + diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/55-bgpd-rm-assert.patch --- /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 +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 + + * 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 + + * 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); diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/60-bgp-comm-crash.patch --- /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 +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 + + * (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", + . + * 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; diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/65-isisd-iso-checksum.patch --- /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 ++ * 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 + + * 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 + #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 @@ + + + +- ++ + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/70-isisd-trill.patch --- /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 ++#include ++ + #include + #include + #include +@@ -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 ++#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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#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 ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++#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<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< 0) \ ++ ((p)[VLANBYTE(v)] |= 1< 0) \ ++ ((p)[VLANBYTE(v)] &= ~(1<> 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 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/75-privs-basicprivset.patch --- /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); diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/80-ripngd-getopt.patch --- /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; diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/85-remove-fwding-dependency.patch --- /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' /> + + +- +- +- +- + + + + +- +- +- +- + + + + +- +- +- +- + + + + +- +- +- +- + + + + +- +- +- +- +- +- +- +- + + + + +- +- +- +- +- +- +- +- + + + ++ ++ ++ + + + diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/patches/90-fix-manpages --- /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 diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/quagga.license --- /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 + 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 + Copyright (C) 2000 Vladimir B. Grebenschikov + Copyright (C) 1994 X Consortium + Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + 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 (davem@redhat.com) + 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. diff -r d3ce52a8aecd -r 7c10b5cba79b components/quagga/quagga.p5m --- /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. +# + + set action.hash %<\1>/man/man8/%<\2>.8 > + edit path man/man8 man/man1m > + 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